Skip to content

Key OO Principles

Key OOP Principles

Encapsulate what varies

  • Definition: Identify the aspects of your applications that vary and separate them from what stays the same. If a component or module in your application is bound to change frequently, then it’s a good practice to separate this part of code from the stable ones so that later we can extend or alter the part that varies without affecting those that don’t vary.
  • Example: In the code below, the pet type varies, and the pet sound is a detail/functionality that varies from pet to pet or even owner to owner as is the pet type itself. Hence we encapsulate the varying details into classes or methods, which allows changing code Block 1 into code Block 2, the speak method encapsulates the varying pet types and sounds:
1
2
3
4
5
6
7
8
// Block 1
if (pet.type() == dog) {
  pet.bark();
} else if (pet.type() == cat) {
  pet.meow();
} else if (pet.type() == duck) {
  pet.quack()
}
1
2
// Block 2
pet.speak();

Favor composition over inheritance

  • Definition: Favor has-a relationships, Composition, over is-a relationships, Inheritance. Inheritance can make code more rigid and less extensible when overused, especially with Java’s single inheritance. Class explosion due to needing to represent composite classes will arise unless composition is used instead (Composite and Builder patterns come to mind).
  • Example: Consider shape classes like Square, Rectangle, Circle inheriting from a Shape class. If we want to make a new shape combining rectangle and square we’d need a RectSquare or a SquareRect class using inheritance, but if we use composition instead, we could make a single CompositeShape class which would be composed of a list of other Shape.

Program to interfaces, not implementations

  • Definition: Program to a super type like an interface or abstract class, not a concrete class.
  • Example 1: Polymorphism, we rely on an interface to allow us to treat different objects that perform similar actions in the same way.
  • Example 2: Assume a database access layer in your application which is used to perform CRUD operations on your DB. Let’s consider that we implement a Service class which calls the DatabaseClient class (However practically we should have a DataAccessor class between Service and DatabaseClient). The DatabaseClient is concrete class programmed to access postgres DB. The DatabaseClient is a heavy duty class with all helper methods required to access the DB. Assume that the client decides to switch to a NoSQL database like MongoDB or add it as a secondary database for some specific purposes. This would lead to rewriting the DatabaseClient which would complicate things.

Strive for loosely coupled designs between objects that interact

  • Definition: A loosely coupled design is one in which classes are weakly associated with each other, and thus changes in one class least affect existence or function of another class.
  • Example: Observer Design Pattern’s Subject knows nothing about Observer implementations waiting for its notifications, just that they implement the Observer interface.

Open Closed Principle

  • The goal is to allow classes to be easily extended to incorporate new behavior without modifying existing code. If we are successful in accomplishing this, designs are resilient to change and flexible enough to take on new functionality and meet changing requirements.
  • Open for extension, but the original code is closed for modification

Dependency Inversion Principle

  • Depend on abstractions. Do not depend on concrete classes.

Principle of Least Knowledge

  • Talk only to your immediate friends.
  • When you are designing a system, for any object, be careful of the number of classes it interacts with and also how it comes to interact with those classes.
  • This principle prevents us from creating designs that have a large number of classes coupled together so that changes in one part of the system cascade to other parts.
  • When you build a lot of dependencies between many classes, you are building a fragile system that will be costly to maintain and complex for others to understand.

How to follow Principle of Least Knowledge

  • Invoke only methods that belong to:
    • The object itself
    • Objects passed in as a parameter to the method
    • Any object the method creates or instantiates
    • Any components of the object

Hollywood Principle

  • Don’t call us, we’ll call you.
  • Guides us to put decision making in high-level modules that can decide how and when to call low-level modules.

Single Responsibility

  • A class should have only one reason to change.
  • Consider that Collections shouldn’t both manage the collection and have the responsibility of traversing that collection.