DEV Community

Ananyaa πŸŽ™οΈ
Ananyaa πŸŽ™οΈ

Posted on • Edited on

Design Patterns

Pertaining a good knowledge of strong Object Oriented(OO) topics is surely a great strength to have but considering knowing these concepts without having proper idea of OO Design Patterns can lead you to have some disastrous roadblocks when working on a real world application.

Here I have created a glossary for few known handy Design Patterns that can help us make our code more flexible, reusable an efficient. Hope it helps!

Patterns aren’t invented, they are discovered


Pre-requisites

1. Encapsulation : object-oriented procedure of combining the data members and data methods of the class inside the user-defined class.

2. Abstraction : the process of hiding certain details and showing only essential information to the user. Achieved using either abstract classes or interfaces.

3. Polymorphism : the ability of an object to take many forms. This helps us perform the same action in different ways.

4. Inheritance : mechanism in which one object acquires all the properties and behaviors of a parent object.


Behavior Patterns

1. Strategy Pattern
Defines a family of algorithms, encapsulates each one, and makes them interchangeable. Strategy lets the algorithm vary independently from clients that use it .

Implementation
Let's say we have a class Demo which needs to perform few operations dynamically. We create a Rundemo class which uses a Strategy interface implemented by different operation classes say Add(), Subtract() and Multiply() .

Step 1
Creating the interface:Strategy.java

public interface Strategy {
   public int operation(int num1, int num2);
}

------------------------------------------------------------------
Step 2
Create concrete classes implementing the same interface: OperationAdd.java

public class Add implements Strategy{
   @Override
   public int operation(int num1, int num2) {
      return num1 + num2;
   }
}

------------------------------------------------------------------
OperationSubstract.java

public class Subtract implements Strategy{
   @Override
   public int operation(int num1, int num2) {
      return num1 - num2;
   }
}

------------------------------------------------------------------
OperationMultiply.java

public class Multiply implements Strategy{
   @Override
   public int operation(int num1, int num2) {
      return num1 * num2;
   }
}

------------------------------------------------------------------
Step 3
Create Rundemo Class: Rundemo.java

public class Rundemo {
   private Strategy strategy;

   public Rundemo(Strategy strategy){
      this.strategy = strategy;
   }

   public int executeStrategy(int num1, int num2){
      return strategy.operation(num1, num2);
   }
}

------------------------------------------------------------------
Step 4
Use the Rundemo to see change in behavior when it changes its Strategy: Demo.java

public class Demo {
   public static void main(String[] args) {
      Rundemo rundemo = new Rundemo(new Add());     
      System.out.println("8 + 5 = " + context.executeStrategy(8, 5));

      rundemo = new Rundemo(new Substract());       
      System.out.println("8 - 5 = " + context.executeStrategy(8, 5));

      rundemo = new Rundemo(new Multiply());        
      System.out.println("8 * 5 = " + context.executeStrategy(8, 5));
   }
}

------------------------------------------------------------------
Step 5
Verify the output.

8 + 5 = 13
8 - 5 = 3
8 * 5 = 40
Enter fullscreen mode Exit fullscreen mode

Key Points to remember

  • Favor composition over inheritance.
  • Encapsulate what varies.
  • Have a clear picture of HAS-A , IS-A relationships before implementing.
  • Program to interfaces not implementations.

2. Observer Pattern
Defines a one-to-many dependency between objects so that when one object changes state, all of its dependents are notified and updated automatically. The use cases that involve updating, deletion and registration of multiple observers dynamically handled by one source often involves utilization of this pattern .

Implementation
Let's say we have a class Demo which is a system that needs to perform few operations(e.g updating subject state) dynamically of a number of observers with the help of a Subject . We have created an abstract class Observer and a concrete class Subject that is extending class Observer. The class Demo uses subject class concrete class object to show observer pattern in action.

Step 1
Create Subject class : Subject.java

import java.util.ArrayList;
import java.util.List;
public class Subject {

   private List<Observer> observers = new ArrayList<Observer>();
   private int state;

   public int getState() {
      return state;
   }

   public void setState(int state) {
      this.state = state;
      notifyAllObservers();
   }

   public void attach(Observer observer){
      observers.add(observer);      
   }

   public void notifyAllObservers(){
      for (Observer observer : observers) {
         observer.update();
      }
   }    
}

------------------------------------------------------------------
Step 2
Create Observer class : Observer.java

public abstract class Observer {
   protected Subject subject;
   public abstract void update();
}

------------------------------------------------------------------
Step 3
Create concrete observer classes : BinaryStateStringObserver.java

public class BinaryStateStringObserver extends Observer{

   public BinaryStateStringObserver(Subject subject){
      this.subject = subject;
      this.subject.attach(this);
   }

   @Override
   public void update() {
      System.out.println( "Binary String: " + Integer.toBinaryString( subject.getState() ) ); 
   }
}

------------------------------------------------------------------
OctalStringStateObserver.java

public class OctalStringStateObserver extends Observer{

   public OctalStringStateObserver(Subject subject){
      this.subject = subject;
      this.subject.attach(this);
   }

   @Override
   public void update() {
     System.out.println( "Octal String: " + Integer.toOctalString( subject.getState() ) ); 
   }
}

------------------------------------------------------------------
HexaStateStringObserver.java

public class HexaStateStringObserver extends Observer{

   public HexaStateStringObserver(Subject subject){
      this.subject = subject;
      this.subject.attach(this);
   }

   @Override
   public void update() {
      System.out.println( "Hex String: " + Integer.toHexString( subject.getState() ).toUpperCase() ); 
   }
}

------------------------------------------------------------------
Step 4
Use Subject and concrete observer objects : Demo.java

public class Demo {
   public static void main(String[] args) {
      Subject subject = new Subject();

      new HexaStateStringObserver(subject);
      new OctalStateStringObserver(subject);
      new BinaryStateStringObserver(subject);

      System.out.println("First state change: 10"); 
      subject.setState(10);
      System.out.println("Second state change: 12");    
      subject.setState(12);
   }
}

------------------------------------------------------------------
Step 5
Verify the output.

First state change: 10
Hex String: A
Octal String: 12
Binary String: 1010
Second state change: 12
Hex String: C
Octal String: 14
Binary String: 1100
Enter fullscreen mode Exit fullscreen mode

Key Points to remember

  • Strive for loosely coupled designs between objects that interact
  • Subjects update Observers using a common interface
  • Observers are loosely coupled in that the Subject knows nothing about them, other than that they implement the Observer interface.

To be continued ...

Top comments (1)

Collapse
 
andrewbaisden profile image
Andrew Baisden

Good brief intro keep the good content flowing.