Sunday 6 April 2014

OOP Design - Part 2 - Class principles - SOLID

I hope, you have already read "Preface" of this post.  Also  don't miss to refer corresponding example java code (not for production, illustrative purposes only) to understand theory of each principle.

SOLID Principles

The principles of SOLID are guidelines that can be applied while working on software to remove code smells by causing the programmer to refactor the software's source code until it is both legible and extensible. It is typically used with test-driven development, and is part of an overall strategy of agile and adaptive programming.
  • S = SRP = Single responsibility principle
  • O = OCP = Open/closed principle
  • L = LSP = Liskov substitution principle
  • I = ISP = Interface segregation principle
  • D = DIP = Dependency inversion principle

Single responsibility principle
  • Definition of SRP?
    • Every class should have a single responsibility, and that responsibility should be entirely encapsulated by the class. (Wikipedia)
    • There should never be more than one reason for a class to change. (Robert C. "Uncle Bob" Martin)
  • Rules of Thumb?
    • If you cannot come up with meaningful name for your class focused to single responsibility, then it's probably doing too much.
    • Ensure you don't design and implement God class (a class that knows too much or does too much, which is example of an anti-pattern).
    • Remember more classes != more complexity
  • Don't mix-up many different responsibilities in a single class (known as God class), instead come up different classes and each should be focused to single responsibility. Example of responsibilities?
    • Validation (PasswordValidation, EmailValidation…)
    • Notification (EmailNotification, SMSNotification…)
    • Parsing (XMLParser, CSVParser...)
    • Formatting
    • Error Handling
    • Persistence
  • Understand theory from Example java code snippet

Open/closed principle
  • Definition of OCP?
    • Software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification. That is, such an entity can allow its behavior to be modified without altering its source code. This is especially valuable in a production environment, where changes to source code may necessitate code reviews, unit tests, and other such procedures to qualify it for use in a product: code obeying the principle doesn't change when it is extended, and therefore needs no such effort. (Wikipedia)
  • Rules of Thumb?
    • Open to extension =  you should design your classes so that new functionality can be added as new requirements are generated.
    • Closed for modification = Once you have developed a class you should never modify it, except to correct bugs.
    • Design and code should be done in a way that new functionality should be added with minimum or no changes in the existing code
    • When needs to extend functionality - avoid tight coupling, don't use if-else/switch-case logic, do code refactoring as required...
    • Techniques to achieve - Inheritance, Polymorphism, Generics
    • Pattern to apply – Strategy Pattern, Template Method
  • Example
    • File Parser  with initially supported Text and XML parsing functionality. Now extend it to support new functionality of CSV parsing and then support even more types of parting...
  • Understand theory from Example java code snippet

    Liskov substitution principle
    • Definition of LSP?
      • Derived classes must be substitutable for their base classes. That means, functions that use pointers or references to base classes must be able to use objects of derived classes without knowing it. (Robert C. "Uncle Bob" Martin)
    • Rules of Thumb?
      • This principle applies to inheritance hierarchies and is just an extension of the Open Close Principle.
      • It means that we must make sure that new derived classes are extending the base classes without changing their original behavior. Basically, derived classes should never do less than their base class.
      • If a subtype of the supertype does something that the client of the supertype does not expect, then this is in violation of LSP. Imagine a derived class throwing an exception that the superclass does not throw, or if a derived class has some unexpected side effects. One has to consider that how the client programs are using the class hierarchy. Sometimes code refactoring is required to fix identified LSP violations.
    • Example
      • Media Player is super class having ability of playing audio and video both. Now VLC and DIV Media Players are the subtypes of Media Player and inheriting the original behavior of Media player; and are substitutable for their base class Media player in the client program code. Now there is need of launching new Winamp Media Player, but with  audio support only. So one could extend Media Player class and override original behavior of Media Player's playVideo() method  for doing nothing or may be throw UnSupportedException (in short Winamp media player is doing less that its super class Media player). So this can be considered as LSP violation, as it may cause unpredictable behavior when Media Player is substituted with Winamp Media Player in client code.
    • Understand theory from Example java code snippet
    Interface segregation principle
    • Definition of ISP?
      • Make fine grained interfaces that are client specific.  That means, clients should not be forced to depend upon interfaces that they do not use. (Robert C. "Uncle Bob" Martin)
    • Rules of Thumb?
      • Don’t depend on things you don’t need. Interfaces containing methods that are not specific to it are called polluted or fat interfaces. We should avoid them.
      • Many client-specific interfaces are better than one general-purpose interface. When we have non-cohesive interfaces, the ISP guides us to create multiple, smaller, cohesive interfaces.
    • Example
      • Consider Order Service interface having responsibilities like createOrder, amendOrder, submitOrder and processOrder. Now there are two clients end users and backend order processor job, who depend on Order Service interface. But processOrder() is not of use for end user client, still client is forced to depend as per design. Similarly backend order process job is only interested in processOrder() service, still forced to depend on other services too. So ISP is violated.
    • Understand theory from Example java code snippet

    Dependency inversion principle
    • Definition of DIP?
      • Depend on abstractions, not on concretions.  (A) High-level modules should not depend on low-level modules. Both should depend on abstractions. (B) Abstractions should not depend on details. Details should depend on abstractions. (Robert C. "Uncle Bob" Martin)
    • Rules of Thumb?
      • Design by contract.
      • Every dependency in the design should target an interface, or an abstract class. No dependency should target a concrete class.
      • Factories and Abstract Factories can be used as dependency frameworks, but there are specialized frameworks for that such as Spring IOC (Inversion of Control Container).
    • Example
      • LoginManager depends on implementation class SimpleAuthenticator to authenticate user from database. In future to change it to authenticate by LDAPAuthenticator, LoginManager and its test case would change. This can be avoided by following Dependency Inversion Principle.
      • The refactored code can be - LoginManager depends on Authenticator interface only. Authenticator interface will be implemented by SimpleAuthenticator and LDAPAuthenticator implementation classes. The client of LoginManager may inject dependency on implementation class or dependency frameworks may be used for it.
    • Understand theory from Example java code snippet


    Also Refer

    No comments:

    Post a Comment