10 Object-Oriented Theories That Java Programmers Should Know

by anonymous on 2013-11-16 16:10:45

### English Translation:

**Title: 10-object-oriented-design-principles**

Object-oriented theory is at the core of object-oriented programming. However, I've noticed that most Java programmers are enthusiastic about design patterns like Singleton, Decorator, or Observer, but they don't pay enough attention to learning object-oriented analysis and design. It's crucial to learn the fundamentals of object-oriented programming (such as abstraction, encapsulation, polymorphism, inheritance), and it's equally important to use them to design clean modules. I also know many programmers of different levels who haven't heard of these object-oriented theories, or don't understand the benefits of a certain design theory, or how to apply these theories in their code.

At the very least, we should aim to design highly cohesive and loosely coupled code. The source code from Apache and Sun serves as an excellent example for learning Java object-oriented theory. JDK follows some design patterns, such as using Factory pattern in BorderFactory, Singleton pattern in the Runtime class, and Decorator pattern in many classes in java.io. If you're truly interested in Java programming, start by reading Joshua Bloch's *Effective Java*, which he helped write while contributing to the Java API. Two other books I recommend on design patterns are *Head First Design Patterns* and *Head First Object-Oriented Analysis and Design* by Kathy Sierra et al. These books help in understanding object-oriented theory and have helped me write better code.

The best way to learn any design theory or pattern is through real-world examples. This article aims to introduce object-oriented theory to programmers who are still learning. Each of the following principles could be elaborated in its own article, and I plan to do so in the future. For now, here’s a quick overview.

**Avoid Repetition - DRY (Don’t Repeat Yourself)**

The first principle of object-oriented design is to avoid repetition. Don't write duplicate code; try to place common functionality in one place. If you plan to write the same code in different places, write it as a single method. If you hard-code a value more than once, declare it as a public final constant. The advantage of this approach is easier maintenance. However, don't overuse this principle. Repetition refers not to code duplication but to functional duplication. For instance, if you have the same piece of code validating OrderID and SSN, but they represent different meanings, combining these two unrelated functionalities can lead to issues. If the format of OrderID changes, the SSN validation code might break. Be cautious of such coupling and don't combine similar but unrelated code.

**Encapsulate What Changes**

In the software world, the only constant is "change." Therefore, it's best to encapsulate parts of your code that you think will change in the future. The benefit of doing so is that it makes the encapsulated code easier to test and maintain. You should declare variables as private first and expand access rights (like making them protected) only when necessary. Many design patterns in Java utilize encapsulation. The Factory design pattern is an example of encapsulation—it encapsulates object creation, allowing new "products" to be introduced without modifying existing code.

**Open-Closed Design Principle**

Classes, methods, and functionalities should be open for extension (new features) but closed for modification. This is another elegant "SOLID" design principle ensuring that tested code isn't altered unnecessarily. If you want to add new features, you must test them separately, which is the goal of the Open-Closed design principle. Additionally, Open-Closed principle stands for 'O' in SOLID.

**Single Responsibility Principle (SRP)**

The Single Responsibility Principle is another "SOLID" design principle representing the 'S'. A class should have only one reason to change, or in other words, a class should only perform a single function. If you place more than one function in a class, it couples those functions together. Changing one function may disrupt the other, requiring more testing to ensure smooth deployment.

**Dependency Injection or Inversion Principle**

Containers provide dependency injection, and Spring offers an excellent implementation of dependency injection. The beauty of this principle is that each injected class is easy to test and maintain because all object creation code is centralized in the container. There are multiple ways to perform dependency injection, such as bytecode instrumentation used by AOP frameworks like AspectJ, or proxies used in Spring. Here's an example of dependency injection. This principle represents the 'D' in SOLID.

**Favor Composition Over Inheritance**

If possible, favor composition over inheritance. Some might disagree, but I’ve found that composition offers more flexibility than inheritance. Composition allows runtime flexibility by setting properties and combining classes via interfaces. We can use polymorphism to change class behavior dynamically, increasing flexibility significantly. *Effective Java* also leans towards using composition.

**Liskov Substitution Principle (LSP)**

According to the Liskov Substitution Principle, subclasses must be substitutable for their base classes. This means that methods that work with instances of the base class should work just as well with instances of the subclass. LSP is closely related to the Single Responsibility Principle and the Interface Segregation Principle. If a base class has more functionality than a subclass can support, the subclass violates LSP. To adhere to LSP, a subclass should enhance rather than reduce the functionality of the base class. LSP represents the 'L' in SOLID.

**Interface Segregation Principle (ISP)**

The Interface Segregation Principle emphasizes that clients should not be forced to depend on interfaces they do not use. When an interface contains more than one functionality, and a client needs only one part of it, the client should not implement unnecessary parts. Designing interfaces is complex because once released, it becomes difficult to modify them without breaking existing implementations. Another benefit of segregating interfaces is that fewer methods need to be implemented if the interface contains only one functionality.

**Program to Interfaces, Not Implementations**

Program to interfaces whenever possible, providing flexibility to introduce new interfaces easily. Use interface types for variable types, return types of methods, and parameter types. Many programmers recommend this practice, including books like *Effective Java* and *Head First Design Patterns*.

**Delegation Principle**

Don't do everything yourself; delegate tasks to appropriate classes when needed. A classic example of using the delegation pattern is the `equals()` and `hashCode()` methods. Instead of comparing objects using client-side code, we let the objects compare themselves. The advantage of this approach is reduced code duplication and easier modification of behavior.

All these object-oriented principles help you write more flexible, highly cohesive, and loosely coupled code. Learning the theory is the first step, but the key is applying these design principles effectively. Identify areas where these principles are violated, but remember that nothing in this world is perfect. Don't try to solve every problem with design patterns and theories, as they are often aimed at large enterprise-level projects with longer lifecycles. In short, smaller projects may not warrant such extensive efforts.

**Translated by:** ImportNew.com - Tang Xiaojuan

**Translated Link:** http://www.importnew.com/6445.html