Object-Oriented Programming
Learning Objectives
- Define objects, classes, state, and behavior in object-oriented programming.
- Explain the commonly taught ideas of encapsulation, abstraction, inheritance, and polymorphism.
- Distinguish a class definition from its runtime instances.
- Compare Java abstract classes and interfaces using language-specific rules.
- Describe SOLID as a set of design heuristics and recognize their trade-offs.
- Read class compartments and common relationship marks in a UML class diagram.
An introduction to object-oriented program structure, class and object terminology, Java examples of abstract classes and interfaces, design heuristics, and UML class-diagram notation.
Object-Oriented Programming (OOP)
Object-oriented programming (OOP) is a programming paradigm that organizes some or all of a program around objects. In a class-based language, objects are instances whose state and operations are described by a class. OOP design does not have to mirror real-world entities, and language features alone do not guarantee secure or maintainable software.
1. The Need for OOP
Procedural programming commonly organizes work around procedures and the data they process. Object-oriented programming organizes some responsibilities around objects that combine state with operations. Either style can be modular and maintainable; either can become difficult to change when dependencies are poorly managed. The useful choice depends on the problem, language, and design constraints.
1.1 The Four Pillars of OOP
Core Principles
- Encapsulation: Grouping state with operations and controlling which code can access members directly. Access modifiers define visibility rules in a language; methods can also validate proposed changes. Visibility alone does not enforce an object's valid-state rules.
- Abstraction: Exposing the operations relevant to a caller while keeping implementation details behind that interface. For example, a caller can request that a file be exported without depending on how a particular exporter formats the data.
- Inheritance: Defining a more specialized type from a base type. Inheritance can share behavior, but it also couples the subtype to the base type; use it when the subtype can satisfy the base type's expectations.
- Polymorphism: In class-based Java, a reference typed as an interface or superclass can refer to a subtype object. For an overridden instance method, runtime dispatch selects the implementation belonging to the object's class.
Access control and validation do different jobs
This Java-style example separates visibility from validation. A private field limits direct access from outside the class, while the setWidth method checks the proposed value before changing the stored state.
One interface call can reach different implementations
The client depends on the shared Exporter type. The actual object's class supplies the implementation of the overridden export method. This example illustrates Java-style runtime dispatch.
Interactive Simulation
Interact with the simulation below to explore the relationship between a class blueprint and its instantiated objects.
The Class (Blueprint)class Car
A class defines the properties and behaviors that all objects of this type will have.
Objects (Instances)
2. Classes and Objects
Class
In a class-based language, a class is a type definition that describes the data and operations available on its instances.
Object
An object is a runtime entity with state, identity, and behavior as defined by its language model. In a class-based language, an object is commonly an instance of a class; its internal representation is language- and runtime-dependent.
Class Terminology
- Fields or properties: Named data associated with a class or its instances. Instance fields can hold per-object state; static fields, where supported, belong to the class or type.
- Methods: Operations associated with a class or object. Some methods read state, some update it, and some return a result.
- Constructor: A language-specific initialization operation associated with creating a class instance. In Java, a constructor runs as part of normal instance creation with
new; other languages and construction paths have different rules. Do not assume every object-oriented language invokes a constructor in the same way.
3. Advanced Architecture: Interfaces and Abstract Classes
Abstract classes and interfaces let a Java design express shared implementation or a type contract. They help communicate constraints, but the language does not guarantee that an entire architecture is consistent or correct.
3.1 Abstract Classes
Abstract Class
In Java, an abstract class is a class that cannot be instantiated directly. It may contain instance fields, implemented methods, constructors, and abstract methods. A concrete descendant must implement inherited abstract methods; an abstract descendant may leave some of those methods abstract.
Abstract Class Example
An abstract Document class could store a document identifier and implement a shared saveDraft() operation while declaring render() abstract. A concrete document type supplies the rendering behavior. An intermediate abstract subclass could defer that implementation.
3.2 Interfaces
Interface
In Java, an interface is a reference type that defines operations and other permitted members. It can declare abstract methods and can also contain default and static methods. A concrete class that implements an interface must satisfy its inherited abstract-method obligations, including obligations already supplied by inherited methods where applicable.
Abstract Class vs. Interface
- Abstract class: In Java, a class can extend one superclass. An abstract superclass is useful when related classes share instance state or implementation as well as a common type.
- Interface: In Java, a class can implement multiple interfaces. An interface can express a capability shared by classes that do not share a superclass; it may also contain default behavior.
Java abstract classes and interfaces serve different roles
This Java-specific comparison shows one superclass for shared state or implementation and multiple interfaces for types or capabilities. Concrete descendants implement inherited abstract methods; abstract descendants may defer them.
4. SOLID Principles of Object-Oriented Design
SOLID is a commonly taught acronym for five object-oriented design heuristics. They can help reviewers discuss responsibilities, extension, substitution, interface size, and dependency direction. Apply them as context-sensitive questions: using them mechanically can add needless layers or abstractions.
The SOLID Acronym
- Single Responsibility Principle (SRP): Keep a module focused on a cohesive responsibility or source of change. For example, calculation rules and file persistence may change for different reasons and can be separated when that boundary is useful.
- Open/Closed Principle (OCP): Prefer extension points when new behavior is likely, so stable code needs fewer edits. Changes to existing code are still appropriate when a design must evolve.
- Liskov Substitution Principle (LSP): A subtype should preserve the behavioral expectations callers rely on for its base type. Method names alone do not establish substitutability; preconditions, results, and side effects matter.
- Interface Segregation Principle (ISP): Avoid making a client depend on operations it does not use. Split an interface when its clients need genuinely different capabilities, while avoiding needless fragmentation.
- Dependency Inversion Principle (DIP): Keep high-level policy from depending directly on low-level implementation details. Both can depend on a suitable abstraction; the right abstraction should reflect a real boundary in the design.
5. Unified Modeling Language (UML) Basics
The Unified Modeling Language (UML) defines standardized notations, including class diagrams that can communicate selected types, members, and relationships. A diagram is one design or documentation view; it does not specify every behavior or guarantee a sound architecture.
UML Class Notation
- Class compartments: A class box may show a name, attributes, and operations in separate compartments. A diagram can omit compartments or members when they are not relevant.
- Visibility marks:
+means public,-private, and#protected in UML notation. Their precise access behavior depends on the implementation language and its rules. - Generalization: A solid line with a hollow triangle points from a specialized classifier toward its general classifier.
- Interface realization: A dashed line with a hollow triangle points from a realizing classifier toward the interface.
- Association and multiplicity: A plain association connects related classifiers. Multiplicity such as
1or0..*is written near each association end to state the modeled count constraint. - Composite aggregation (composition): A filled diamond is placed at the composite, or whole, end. UML gives this strong ownership semantics: a part belongs to at most one composite at a time, and object parts are deleted with the composite. The implementation should match that intended lifecycle.
- Shared aggregation: A hollow diamond marks shared aggregation. Explain the intended meaning in the model; the notation alone does not define a universal lifecycle policy.
Read UML class compartments and relationship marks
The diagram summarizes common UML class notation. A filled diamond sits at the whole end of a composite association; multiplicities are placed by association ends. A UML diagram communicates the structure selected by its author, not every implementation detail.
- OOP organizes some or all program responsibilities around objects; it does not require every program to use the same design style.
- Encapsulation combines state and operations with access rules; methods can enforce state constraints when they validate updates.
- Classes describe instance structure in class-based languages, while runtime object representation depends on the language and implementation.
- In Java, abstract classes can share state and implementation, and interfaces can define types with abstract and default methods.
- Java selects an overridden instance-method implementation using the runtime object's class when called through a superclass or interface reference.
- SOLID offers design heuristics for discussing change, substitutability, interface boundaries, and dependency direction; judgment remains necessary.
- UML class diagrams use compartments, visibility, relationships, and multiplicities to communicate selected structural decisions.