mahyarevafa

اینجا کلیه Design Pattern های Gang Of Four بصورتی ساده و قابل درک تر از اغلب منابع موجود تشریح شده اند.



درباره Design Pattern ها:

دانستن Design Pattern ها از ملزومات اصلی کار هر طراح نرم افزار است. Design Pattern ها عبارتند از Best Practice های طراحی که هر کدام در مساله خاصی کاربرد دارند.

این Design Pattern ها حوزه های گوناگونی را در بر می گیرند. برخی زبان ها و فریم ورک های برنامه نویسی با توجه به امکانات خود، Pattern های ویژه ای را ارائه می دهند که لایه های مختلف طراحی را در بر می گیرد.

آنچه در اینجا گردآوری شده شامل همه 23 عدد Design Pattern کلاسیک و اصلی طراحی Object Oriented است که به Design Pattern های Gang of Four (GOF) شهرت دارند و برای اولین بار در کتاب Design Patterns: Elements of Reusable Object-Oriented Software در سال 1994 مطرح شدند. علت شهرت این Design Pattern ها بدین نام به دلیل چهار نویسنده کتاب است. با توجه به مدت درازی که از طرح این Pattern ها گذشته هنوز هم این Design Pattern ها در طراحی مورد استفاده قرار می گیرند و حتی بسیاری از Pattern های دیگر بر پایه آنها بنا می‌شوند. بنابراین دانستن آنها برای هر طراح نرم افزار ضروری است.

برای تهیه این مطالب از منابع مختلفی استفاده شده است. سعی شده از بین همه منابع ساده ترین مثالها گزینش شود و همه حشو و زوائد نیز حدف گردد تا امکان درک Pattern و روش بکارگیری آن هرچه ساده تر میسر شود.


فهرست:

Creational Patterns:
Factory Method
Abstract Factory
Singleton
Prototype
Builder

Structural Patterns:
Adapter
Proxy
Decorator
Bridge
Composite
Facade
Flyweight

Behavioral Patterns:
Template Method
Strategy
Command
Memento
Mediator
State
Observer
Iterator
Chain Of Responsibility
Interpreter
Visitor


Factory Method

GOF intent:
Define an interface for creating an object, but let subclasses decide which class to instantiate. Factory Method lets a class defer instantiation to subclasses.

روش سنتي ساخت instance از کلاس اينگونه است که هرجا لازم باشد از دستور new استفاده نماييم.
مثال:

class UIFrameWork{

   public static UIComponent CreateUI(){
      UIComponent aUI = new UIComponent();
      TextboxComponent aText = new TextboxComponent();
      ButtonComponent aBtn = new ButtonComponent();

      aUI.addTextbox(aText);
      aUI.addButton(aBtn);
      return aUI;
   }
}

اين کار اشکالاتي دارد:

فرض کنيد در مثال فوق بخواهيم علاوه بر UI ساده، UI با قابليت Scroll نيز ارائه دهيم. در اين صورت با وجودي که بخش زيادي از عناصر UI از جمله TextBox و Button بدون تغيير است، اما مجبوريم کد بالا را عينا براي UI جديد نيز بنويسيم:

class UIFrameWork{

   public static UIComponent CreateUI(){
      // Above Code
   }

   public static UIComponent CreateScrollingUI(){
      ScrollingUIComponent aUI = new ScrollingUIComponent();
      TextboxComponent aText = new TextboxComponent();
      ButtonComponent aBtn = new ButtonComponent();

      aUI.addTextbox(aText);
      aUI.addButton(aBtn);
      return aUI;
   }
}

راه حل: استفاده از Factory Method
Factory Method به متدي گفته مي شود که new شدن instance توسط آن انجام مي‌شود.
شکل زير ساختار اجزاي Factory Pattern را نشان مي دهد.

Factory Method Pattern

بازنويسي مثال فوق با استفاده از Factory Method:

class UIFrameWork{

   public static UIComponent makeUIComponent (){
      return new UIComponent;
   }

   public static TextboxComponent makeTextboxComponent(){
      return new TextboxComponent;
   }

   public static ButtonComponent makeButtonComponent (){
   return new UIComponent;
   }

   public static UIComponent CreateUI(){
      UIComponent aUI = makeUIComponent();
      TextboxComponent aText = makeTextboxComponent();
      ButtonComponent aBtn = makeButtonComponent();

      aUI.addTextbox(aText);
      aUI.addButton(aBtn);
      return aUI;
   }
}

class ScrollingUIFrameWork extends UIFramework{

   public static UIComponent makeUIComponent (){
      return new ScrollingUIComponent;
   }
}

ملاحظه مي‌شود که با Extend کردن از کلاس اصلي و Override کردن متد makeUIComponent مي‌توان UI با امکان Scroll ايجاد نمود.
در اين مثال Factory Method ها عبارتند از:

فايده هاي :Factory Method

عيب‌هاي Factory Method:

منبع ها:

1- Design Patterns – Elements of Reusable Object Oriented Software (GOF) By Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides
2- codeproject

بازگشت به فهرست

Abstract Factory

GOF Intent:
Provide an interface for creating families of related or dependent objects without specifying their concrete classes.

از عيب هاي Factory Method Pattern اين است که Creator Concrete، بايد مستقيما در کد ذکر شود. در Abstract Factory اين مشکل وجود ندارد. تفاوت اصلي Factory Method و Abstract Factory همين است.

Abstract Factory Pattern به نام KIT نيز شناخته مي‌شود.

Abstract Factory يک interface يا کلاس abstract است که signature متدهاي ساخت Object ها در آن تعريف شده است و Concrete Factory ها آنها را implement مي‌نمايند.
در Abstract Factory Pattern همه Product هاي هم خانواده در Concrete Factory مربوط به آن خانواده پياده سازي و مجتمع مي‌گردند.
در کدهاي برنامه تنها با Abstract Factory و Abstract Product ها سر و کار داريم و به هيچ وجه درگير اين مساله که کدام يک از Concrete Class ها در برنامه مورد استفاده قرار مي‌گيرند، نمي‌شويم.

شکل زير ساختار Abstract Factory را نشان مي‌دهد:

Abstract Factory Pattern

نمونه کد:

interface Connection(){
   public void doConnect();
}

class OracleConnection implements Connection{
   public void doConnect(){
      // Connect to Oracle DB
   }
}

class SqlServerConnection implements Connection{
   public void doConnect(){
      // Connect to SQL Server DB
   }
}

interface ConnectionFactory {
   public Connection CreateConnection();
}

class OracleConnectionFactory implements ConnectionFactory {
   public Connection CreateConnection(){
      return new OracleConnection();
   }
}

class SqlServerConnectionFactory implements ConnectionFactory {
   public Connection CreateConnection(){
      return new SqlServerConnection();
   }
}

public class ConnectionServices{
   public static ConnectionFactory getConnectionFactory(){
      if (type == DB_TYPE_ORACLE){
         return new OracleConnectionFactory();
      } else if (type == DB_TYPE_SQLSERVER) {
         return new SqlServerConnectionFactory();
   }
}

public class ClientApp{
   public static void main(String[] args){
      ConnectionFactory aConnectionFactory = ConnectionServices.getConnectionFactory();
      Connection aConnection = aConnectionFactory.CreateConnection();
      aConnection.doConnect();
   }
}

فايده هاي Abstract Factory:

عيب Abstract Factory:

تکنيک هاي مفيد در مورد Abstract Factory:

منبع ها:

1- Design Patterns – Elements of Reusable Object Oriented Software (GOF) By Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides
2- Definition of Abstract Factory in Wikipedia

بازگشت به فهرست

Singleton

GOF Intent:
Ensure a class only has one instance, and provide a global point of access to it.

گاهي مي خواهيم مطمئن باشيم که از يک کلاس خاص فقط يک instant مي‌شود ساخت.
يک راه مي تواند استفاده از يک متغير سراسري باشد. اما اين روش از ساخت instant هاي ديگر جلوگيري نمي کند.


راه بهتر اين است که خود آن کلاس اين وظيفه را به عهده گيرد. دياگرام و مثال زير نحوه انجام اين کار را نمايش مي دهند:

Singleton

public class Singleton {

   private static final Singleton instance = new Singleton();

   private Singleton() { }

   public static Singleton getInstance() {
      return instance;
   }
}

در مثال فوق هنگام Load شدن کلاس در JVM، خود instance آن نيز ساخته مي‌شود. حتي اگر هرگز نيازي به آن نباشد و مورد دستيابي قرار نگيرد.
مي‌شود کاري کرد که وقتي instance براي اولين بار مورد نياز است، ساخته شود. به اين حالت Lazy Instantiation گفته مي‌شود. پياده سازي آن چنين است:

public class Singleton {

   private static Singleton instance = null;

   private Singleton() { }

   public static synchronized Singleton getInstance() {
      if (instance == null)
         instance = new Singleton();

      return instance;
   }

}

چند نکته:

  1. در حالت Lazy Instantiation اين امکان وجود دارد که متد getInstance بطور همزمان توسط دو thread فراخوانده شود و بيش از يک instant ساخته شود. براي جلوگيري از اين مساله کليدواژه synchronized به متد اضافه شده است. اين مشکل در روش اول وجود ندارد.
  2. حتي در اين حالت نيز مي توان با دستور
    Singleton clonedObject = (Singleton) obj.clone();
    يک کپي از کلاس ايجاد کرد. براي اينکه از اين کار هم جلوگيري شود بايد متد clone بدين صورت override شود:

public Object clone() throws CloneNotSupportedException {
   throw new CloneNotSupportedException();
}

  1. اگر لازم باشد که بتوان از Singleton يک SubClass تعريف نمود، بايد constructor آن از حالت private به protected تغيير کند. البته اين کار توصيه نمي شود. چون SubClass ها مي توانند، Constructor را override کنند و به حالت public در آورند و بدين ترتيب Singleton را نقض نمايند.
  2. Singleton ها نبايد serialized شوند، چون اگر پس از serialized شدن بيش از يک بار deseriaized شوند، در آن صورت بيش از يک instance از آن خواهيم داشت.

فايده هاي Singleton:

عيب Singleton:
به دليل وجود راه هايي براي دور زدن مفهوم singleton، اين pattern مخالفان زيادي دارد.

استفاده از Singleton در ديگر Design Pattern ها:


منبع ها:

  1. Design Patterns – Elements of Reusable Object Oriented Software (GOF) By Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides
  2. Singleton in wikipedia
  3. Singleton in javabeginner
  4. Singleton in javacoffeebreak
  5. Clone in wikipedia

بازگشت به فهرست

Prototype Design Pattern

GOF Intent:
Specify the kinds of objects to create using a prototypical instance, and create new objects by copying this prototype.

هنگامي که از new براي ساخت کلاس استفاده شود، يک Object کاملا از نو و مطابق مشخصات آن کلاس ساخته مي شود.

اما گاهي خوب است که بتوانيم از يک Object موجود يک کپي تهيه کنيم.

براي درک بيشتر مثال زير را در نظر بگيريد:
فرض کنيد مي خواهيم چند عدد کيک بپزيم. کيک هايي که در اساس مانند هم هستند و همگي از تخم مرغ و آرد و شير و غيره تشکيل شده اند. اما بعضي با سيب تزئين مي شوند و بعضي با آناناس و بعضي با پرتقال. در اينجا راحت تر است يک Prototype از کيک اصلي داشته باشيم و براي همه کيک ها ابتدا يک کپي از روي آن تهيه کنيم و سپس فقط آن را به دلخواه تزئين نمائيم.

استفاده از Clone:
اساس اين امکان، متد Clone است که در زبان هاي مختلف براي کپي کردن Object ها بکار مي رود. نحوه اين کار در زبان جاوا بدين شکل است:

public class CopyMe implements Cloneable{
   public Object clone() {
      try {
         return super.clone();
      } catch (CloneNotSupportedException ex) {
         return null;
      }
   }
}

حال با توجه به Clonable بودن CopyMe براي تهيه کپي از يک نمونه اين کلاس اينگونه عمل مي کنيم:

CopyMe instance1 = new CopyMe();

CopyMe instance2 = (CopyMe) instance1.clone();

دياگرام اين Pattern بدين شکل است:

Prototype Design Pattern

نمونه کد:

public abstract class Vehicle implements Cloneable{
   private String name;

   // Setter & Getter Methods
   ...

   public Object clone() {
      try {
         return super.clone();
      } catch (CloneNotSupportedException ex) {
         return null;
      }
   }
}

public class Car extends Vehicle{
   private String producer;

   private String color;
   private boolean automaticGear = false;

   // Setter & Getter Methods
   ...
}



public static void main(String[] args) {
   Car template_Pride = new Car();
   template_Pride.setName("Pride");
   template_Pride.setProducer("Iran");

   Car template_BMW = new Car();
   template_BMW.setName("BMW");
   template_BMW.setProducer("Germany");

   Car myCar = (Car) template_Pride.clone();
   myCar.setColor("White");

   Car yourCar = (Car) template_BMW.clone();
   yourCar.setColor("Red");
   yourCar.setAutomaticGear(true);

   Car hisCar = (Car) template_BMW.clone();
   hisCar.setColor("Blue");

   System.out.println("I have a "+myCar.getColor()+" "+myCar.getName());
   System.out.println("You have a "+yourCar.getColor()+" "+yourCar.getName());
}

فايده هاي Prototype:



منبع ها:

  1. Design Patterns – Elements of Reusable Object Oriented Software (GOF) By Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides
  2. Prototype design Pattern in codeproject
  3. javabestpractice

بازگشت به فهرست

Builder

GOF Intent:
Separate the construction of a complex object from its representation so that the same construction process can create different representations.

Builder کلاسي است که کارش ساختن يک Product پيچيده است و مراحل ساخت آن Product را در خود نگه مي دارد.

معمولا وقتي از Builder استفاده مي کنيم که ساخت Product از مراحل مختلفي تشکيل شده است.

هر Product داراي Builder مخصوص به خود است.

چه وقت مي توانيم از Builder Pattern استفاده کنيم؟

  1. وقتي که Object هايي داشته باشيم با اجزاي يکسان، اما با نحوه ساخته شدن هاي متفاوت.
  2. وقتي که الگوريتم ساخت يک Complex Object از اجزاي تشکيل دهنده آن مستقل باشد.

تفاوت Builder و Factory:
Factory کلاسي است که مجموعه کلاس هاي مختلف از يک خانواده را ايجاد مي کند.
Builder کلاسي است که مراحل ساخت يک Complex Product را مشخص مي کند.

دياگرام Builder Design Pattern چنين است:

Builder Design Pattern

مثال:
حيوانات داراي سر و دم و بدن و غيره هستند. اما فرم و ترکيب (نحوه ساخت) اين اجزا از حيواني به حيوان ديگر تفاوت مي کند. اگر بخواهيم همين مثال را با Builder Pattern پياده سازي کنيم چنين کدي خواهيم داشت:

public abstract class Animal {
   private String head;
   private String body;
   private String tail;

   // Setter & Getter Methods

   public abstract void eat();

   public void showMe(){
      System.out.println(head);
      System.out.println(body);
      System.out.println(tail);
      eat();
   }
}


public class Monkey extends Animal{
   public void eat(){
      System.out.println("I eat banana!");
   }
}


public class Cat extends Animal{
   public void eat(){
      System.out.println("I eat cat food!");
   }
}


// AnimalBuilder is the Parent of Concrete Builders
public abstract class AnimalBuilder {
   public Animal aAnimal;

   public abstract void buildAnimalHead();
   public abstract void buildAnimalBody();
   public abstract void buildAnimalTail();
}


public class MonkeyBuilder extends AnimalBuilder{

   public void MonkeyBuilder(){
      aAnimal = new Monkey();
   }

   public void buildAnimalHead(){
      System.out.println("This is Monkey head");
   }

   public void buildAnimalBody(){
      System.out.println("This is Monkey body");
   }

   public void buildAnimalTail(){
      System.out.println("This is Monkey tail");
   }
}


public class CatBuilder extends AnimalBuilder{
... // Same as MonkeyBuilder
}


public class Kid{
   public void makeAnimal(AnimalBuilder aAnimalBuilder){
      aAnimalBuilder.buildAnimalHead();
      aAnimalBuilder.buildAnimalBody();
      aAnimalBuilder.buildAnimalTail();
   }
}


public static void main(String[] args){
   Kid aKid = new Kid();
   AnimalBuilder buider1 = new MonkeyBuilder();
   aKid.makeAnimal(buider1);
   builder1.aAnimal.showMe();
}

فايده هاي Builder:


منبع ها:

  1. Design Patterns – Elements of Reusable Object Oriented Software (GOF) By Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides
  2. Builder in codeproject

بازگشت به فهرست

Adapter

GOF Intent:
Convert the interface of the class into another interface clients expect. Adapter lets classes work together that couldn't otherwise because of incompatible interfaces.

گاه لازم است که يک کلاس توسط Client فراخواني شود. اما interface آن کلاس با آنچه مورد انتظار Client است تفاوت دارد. اينجا است که Adapter بکار مي‌آيد.

نام ديگر اين Pattern، Wrapper است.

چه وقت از Adapter design pattern استفاده کنيم؟


Adapter Design Pattern

انواع Adapter:
َAdapter به دو شکل پياده مي شود


در هر دو حالت يک کلاس بعنوان Adapter وجود دارد که متد مورد انتظار Client را پياده مي‌کند. در حالت Object Adapter اين کلاس صرفا متدي از کلاس Adaptee را فراخواني مي کند. اما در حالت Class Adapter، کلاس Adapter لازم است Adaptee را extends نمايد. در نمودارهاي زير تفاوت اين دو نوع پياده سازي قابل مشاهده است:

Object Adapter


Class Adapter

مثال 1:
فرض کنيد کلاس هاي Adaptee و Target بدين صورت هستند:

public class Product{ // Target
   public int getRialPrice();
}


interface ForeignProduct{ // Adaptee
   public int getPrice(); // in Dollar
}

ايجاد Class Adapter:

public class ProductAdapter extends ForeignProduct implements Product{
   public int getRialPrice(){
      return getPrice() * 2000; // 2000 is dollar rate
   }
}

و همين کار به روش Object Adapter:

public class ProductAdapter extends Product{

   ForeignProduct aInstance;

   public int getRialPrice(){
      return aInstance.getPrice() * 2000; // 2000 is dollar rate
   }
}

فارغ از اينکه Adapter با چه روشي پياده شده باشد، بدين صورت توسط Client فراخواني مي گردد:

public static void main(String[] args)
{
   Product product1 = new ProductAdapter();
   int i = product1.getRialPrice();
}

مثال 2:

interface EmployeeInterface{
   public void showEmployeeInfo();
}


public class Employee implements EmployeeInterface{
   private name;

   public Employee(String name){
      this.name = name;
   }

   public void showEmployeeInfo(){
      System.out.println("Employee Name:"+name);
   }
}


public class Consultant{
   private name;

   public Consultant(String name){
      this.name = name;
   }

   public string getName(){
   return name;
   }
}


// Object Adapter Style:
public EmployeeAdapter implements EmployeeInterface{
   private consultant aInstance;

   public EmployeeAdapter(String name){
      aInstance = new Consultant(name);
   }

   public void showEmployeeInfo(){
      System.out.println("Nonsultant Name:"+aInstance.getName());
   }
}


// Class Adapter Style:
public EmployeeAdapter extends Consultant implements EmployeeInterface{

   public EmployeeAdapter(String name){
      super(name);
   }

   public void showEmployeeInfo(){
      System.out.println("Nonsultant Name:"+getName());
   }
}


public static void main(String[] args){
   Arraylist list = new Arraylist();
   list.Add(new Employee("Tom"));
   list.Add(new Employee("Jerry"));
   list.Add(new EmployeeAdapter("Max"));
   for (Iterator it = list.iterator(); it.hasNext();) {
      it.next().showEmployeeInfo();
   }
}

مقايسه Class Adapter و Object Adapter:

مزيت Object Adapter:

مزيت هاي Class Adapter:

منبع ها:

  1. Design Patterns – Elements of Reusable Object Oriented Software (GOF) By Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides
  2. Adapter in codeproject

بازگشت به فهرست

Proxy

GOF Intent:
Provide a surrogate or placeholder for another object to control access to it.

Proxy يک واسط بين Objectاصلي و Client است که مي تواند دسترسي به Object را کنترل کند يا امکانات ديگري به آن اضافه نمايد.
به عبارت ديگر Proxy بين Client و Object اصلي قرار مي گيرد و Client براي کار با Object اصلي ناچار است از Proxy عبور نمايد.

نام ديگر آن Surrogate است.

برخي کاربردهاي Proxy Pattern عبارتند از


نمودار اين Pattern بدين صورت است:

Proxy Design Pattern

نمونه کد:

interface Report{
   public void ShowReport();
}


class FinantialReport implements Report{
   public void ShowReport(){
      ...
   }
}


public class FinantialReportProxy implements Report{
   private Report rep;
   private User aUser;

   public void FinantialReportProxy(User aUser){
      this.aUser = aUser;
      rep = new FinantialReport();
   }

   public void ShowReport(){
      if (aUser.getRole() == USER_ADMIN)
         rep.ShowReport();
      else
         System.out.println("This User does not have permission for Finantial Reports");
   }
}


public static void main(String[] args){
   Report aReport = new FinantialReportProxy(currentUser);
   aReport.ShowReport();
}



منبع ها:

  1. Design Patterns – Elements of Reusable Object Oriented Software (GOF) By Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides

بازگشت به فهرست

Decorator

GOF Intent:
Attach additional responsibilities to an object dynamically. Decorators provide a flexible alternative to subclassing for extending functionality.

Decorator Pattern براي افزودن (Decorate کردن) به قابليت هاي يک Object در زمان اجرا (Run Time)، مستقل از ساير instance هاي آن Object، استفاده مي شود.

Decorator مي تواند جايگزين Subclassing باشد. با اين تفاوت که Subclassing در زمان Design اعمال مي شود، اما Decorator در زمان اجرا.

Wrapper نام ديگر اين Pattern است.

Decorator Design Pattern

بهترين راه براي آشنايي با اين Pattern پيگيري يک مثال است
تعريف کلاس زير را براي Coffee ساده در نظر بگيريد:

Public interface Coffee{
   public int getCost();
   public String getIngredients();
}


public class SimpleCoffee implements Coffee{
   public int getCost(){
      return 100;
   }
   public String getIngredients(){
      return "Coffee";
   }
}

حال مي خواهيم در زمان اجرا کلاس SimpleCofee را با مواد ديگر مثل شير يا شکر به دلخواه Decorate نماييم:

public abstract class CoffeeDecorator implements Coffee{

   protected Coffee decoratedCoffee;

   public CoffeeDecorator(Coffee decoratedCoffee){
      this.decoratedCoffee = decoratedCoffee;
   }

   public int getCost(){
      return decoratedCoffee.getCost();
   }

   public String getIngredients(){
      return decoratedCoffee.getIngredients();
   }
}


public class Milk extends CoffeeDecorator{

   public Milk(Coffee decoratedCoffee){
      super(decoratedCoffee);
   }

   public int getCost(){
   return super.getCost() + 50;
   }

   public String getIngredients(){
      return super.getIngredients() + ", Milk";
   }
}


public class Sugar extends CoffeeDecorator{

   public Sugar(Coffee decoratedCoffee){
      super(decoratedCoffee);
   }

   public int getCost(){
      return super.getCost() + 20;
   }

   public String getIngredients(){
      return super.getIngredients() + ", Sugar";
   }
}


public static void main(String[] args){

   // Createing Simple Cofee
   Coffee c = new SimpleCoffee();

   c = new Milk(c);    //Adding Milk
   c = new Sugar(c);    //Adding Sugar

   System.out.println("Price :"+c.getCost());
   System.out.println("Ingredients :"+c.getIngredients());
}

دو نمونه کاربرد در دنياي واقعي:



منبع ها:

  1. Design Patterns – Elements of Reusable Object Oriented Software (GOF) By Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides
  2. Decorator in Wikipedia

بازگشت به فهرست

Bridge

GOF Intent:
Decouple an abstraction from its implementation so that the two can vary independently.

نام ديگر: Handle/Body

معمولا وقتي که يک Class چندين نوع پياده سازي دارد، ساده ترين راهي که به ذهن مي رسد inheritance است. يعني Class اصلي را بصورت يک Abstract Class يا Interface در نظر بگيريم و Implementation هاي مختلف آن را بعنوان SubClass هايش.

در اين حالت Abstract مستقيما با Implementation ها وابستگي دارد و هر تغيير در يکي بايد در ديگري لحاظ شود.

در ضمن خود Operation هايي که در Abstract بايد درنظر گرفته شوند، گاهي انواع مختلف دارند.

براي رفع اين دو مشکل Bridge Pattern مي تواند کارساز باشد.

براي درک بهتر Bridge Pattern و جاي کاربرد آن مثالي را پي مي گيريم.

مثال:
وسايل خانه مثل تلويزيون و کولر و غيره. هر يک کارکرد مشخص خود را دارند. اما روشن و خاموش کردن آنها با هم شباهت هايي دارد.

Bridge Design Pattern Sample

در اين حالت يک راه اين است که چنين طراحي داشته باشيم:

Bridge Design Pattern Sample2

اين روش اما مشکلاتي دارد:

روش ديگري که ممکن است به نظر برسد اين است که بجاي تعريف Subclass جديد روش هاي مختلف Switch را بصورت متدهاي جداگانه در خود کلاس پياده کنيم.
در اين روش هم به ازاي هر switch جديد ملزم به تغيير تک تک کلاس ها خواهيم بود.

راه ديگر استفاده از Bridge Pattern است.

استفاده از Bridge Design Pattern در مثال بالا:

Bridge Design Pattern Sample 3

ساختار Bridge Pattern بدين صورت است:

Bridge Design Pattern

نمونه کد:

public interface IAppliance{
   public void run();
}


public class TV implements IAppliance{
   public void run(){
      System.out.println("TV is running. ");
   }
}


public class AirConditioner implements IAppliance{
   public void run(){
      System.out.println("AirConditioner is running. ");
   }
}


public abstract class Switch{
   protected IAppliance appliance;
   public abstract void turnOn();
}


public class RemoteControl extends Switch{
   public RemoteControl(IAppliance appliance){
      this.appliance = appliance;
   }

   public void turnOn(){
      appliance.run();
   }
}



public static void main(String[] args){
   IAppliance tv = new TV();
   Switch s = new RemoteControl(tv);
   s.turnOn();
}



منبع ها:

  1. Design Patterns – Elements of Reusable Object Oriented Software (GOF) By Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides
  2. Bridge Pattern in devlake

بازگشت به فهرست

Composite

GOF Intent:
Compose objects into tree structures to represent part-whole hierarchies. Composite lets clients treat individual objects and compositions of objects uniformly.

Composite Pattern براي پياده سازي ساختارهاي درختي بکار مي رود.

وقتي از Composite Pattern استفاده کنيد که مي خواهيد:

  1. سلسله مراتب کل به جزء را پياده نماييد
  2. مي خواهيد Client تفاوتي بين يک Object و ترکيب Object ها قائل نشود و با هر دو، يکسان رفتار نمايد
بطور کلي Composite به Object اي گفته مي شود که مي تواند در ساختار درختي Object هاي ديگري را در زير خود بپذيرد.
بر خلاف آن Leaf است که در پايين درخت قرار دارد و Object ديگري نمي تواند زير آن قرار گيرد.

Composite Tree



ساختار کلاس ها در اين Pattern بدين صورت است:

Composite Design Pattern



نمونه کد:

public interface IEmployee{
   public int showScore();
}


public class Worker implements IEmployee{ // LEAF
   private String name;
   private int score;

   public Worker(String name, int score){
      this.name = name;
      this.score = score;
   }

   public int showScore(){
      System.out.println("Name:"name+", Score:"+score)
   }
}


public class Supervisor implements IEmployee{ // COMPOSITE
   private String name;
   private int score;

   private ArrayList subordinate = new ArrayList();

   public Supervisor(String name, int score){
      this.name = name;
      this.score = score;
   }

   public void addSubordinate(IEmployee employee){
      subordinate.add(employee);
   }

   public int showScore(){
      System.out.println("Name:"name+", Score:"+score);

      for (Iterator it = subordinate.iterator(); it.hasNext();) {
         it.next().showScore();
      }
   }
}


public static void main(String[] args){

   Worker a = new Worker("Ali", 5);
   Supervisor b = new Supervisor("Reza", 6);
   Supervisor c = new Supervisor("Hamid", 7);
   Supervisor d = new Supervisor("Amir", 9);
   Worker e = new Worker("Nader", 3);

   b.AddSubordinate(a);
   c.AddSubordinate(b);
   c.AddSubordinate(d);
   d.AddSubordinate(e);

   c.showScore();
}



منبع ها:

  1. Design Patterns – Elements of Reusable Object Oriented Software (GOF) By Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides
  2. Composite in Devlake

بازگشت به فهرست

Facade

GOF Intent:
Provide a unified interface to a set of interfaces in a subsystem. Façade defines a higher level interface that makes the subsystem easier to use.

Façade عبارت است از يک interface ساده شده از چند library يا Subsystem. با استفاده از آن پيچيدگي هاي دروني پياده سازي و فراخواني Subsystem ها از ديد کاربر پنهان مي گردد.

Façade در واقع بعنوان Entry point آن Subsystem عمل مي کند و وابستگي‌ها کاربر را از خود Subsystem و پياده سازي آن از بين مي برد

Façade همچنين فراخواني متدهاي موجود در Subsystem را نظم مي بخشد.

Facade Design Pattern

نمونه کد:

public class Class1{
   public void doTask1(){
      System.out.println("Doing task 1");
   }
}


public class Class2{
   public void doTask2(){
      System.out.println("Doing task 2");
   }
}


public class Class3{
   public void doTask3(){
      System.out.println("Doing task 3");
   }
}


public class Facade{
   public void doSomething(){
      Class1 a = new Class1();
      Class2 b = new Class2();
      Class3 c = new Class3();

      a.doTask1();
      b.doTask2();
      c.doTask3();
   }
}


static static void main(String[] args){
   Facade f = new Facade();
   f.doSomething();
}

و نمونه کد 2:
وقتي که دکمه استارت کامپيوتر را مي زنيم، اجزاء مستقل از هم مثل حافظه و هارد و CPU دست به دست مي دهند تا کامپيوتر روشن شود. در اين حالت دکمه استارت درواقع نقش Facade را بازي مي کند.

/* Complex parts */
class CPU {
   public void freeze() { ... }
   public void jump(long position) { ... }
   public void execute() { ... }
}


class Memory {
   public void load(long position, byte[] data) { ... }
}


class HardDrive {
   public byte[] read(long lba, int size) { ... }
}


/* Facade */
class Computer {
   private CPU cpu;
   private Memory memory;
   private HardDrive hardDrive;

   public Computer() {
      this.cpu = new CPU();
      this.memory = new Memory();
      this.hardDrive = new HardDrive();
   }

   public void startComputer() {
      cpu.freeze();
      memory.load(BOOT_ADDRESS,
         hardDrive.read(BOOT_SECTOR,SECTOR_SIZE));
      cpu.jump(BOOT_ADDRESS);
      cpu.execute();
   }
}


/* Client */
public static void main(String[] args) {
   Computer facade = new Computer();
   facade.startComputer();
}



منبع ها:

  1. Design Patterns – Elements of Reusable Object Oriented Software (GOF) By Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides
  2. Facade in Devlake
  3. Facade in Wikipedia

بازگشت به فهرست

Flyweight

GOF Intent:
Use sharing to support large numbers of fine-grained objects efficiently.

گاهي تعداد زيادي Object داريم که ماهيت يکساني دارند و از بسياري جهات شبيه يکديگر هستند و تنها تفاوت هاي اندکي با هم دارند. Flyweight در اين شرايط استفاده مي شود و باعث کاهش فضاي حافظه مورد استفاده مي گردد.

مثلا فرض کنيد در يک بازي تعدادي موجود يک شکل حمله کننده داريم و فقط محل قرار گيري آنها و رنگ شان متفاوت است و بقيه مشخصات آنها يکسان است.

مثال معروف ديگر Word processor ها هستند که تعداد زيادي کاراکتر با مشخصات يکسان را در قالب خطوط و سطر و ستون ها به نمايش مي گذارند. يک حرف در جاهاي مختلف متن ممکن است استفاده شود، اما در همه جا شکل و ويژگي هاي يکسان دارد.

Flyweight Pattern مي گويد که به جاي نگهداري خود حرف اشاره گر به آن را نگه داريم. بديهي است در اينجا همه حروف بايد در يک Pool نگهداري شوند.

در اينجا دو مفهوم کليدي وجود دارد:

Intrinsic state: عبارت است از چيزهايي از Object که ثابت هستند و مشخصات ذاتي آن به شمار مي آيند.
Extrinsic state: شامل چيزهايي که ثابت نيستند و از يک Object به Object ديگر تفاوت مي کنند.


Flyweight عبارت است از Object اي است که مشخصه هاي يکسان يا همان Intrinsic state در آن پياده مي شود و بصورت Share مورد استفاده قرار مي گيرد.

Flyeweight در لغت به معني سبک وزن و از اصطلاحات ورزش بوکس است.

دياگرام اين Pattern بدين صورت است:

همانگونه که در دياگرام بالا مي شود ديد، لزومي ندارد که همه Subclass هاي Fiyweight بصورت Share استفاده شوند.

در اين مثال فقط Character لازم است Share شود و نمونه هاي آن از داخل يک pool اختصاص داده شوند.

نمونه کد:
فرض کنيد در يک قهوه خانه، انواع قهوه مثل اسپرسو و کاپوچينو و غيره براي ميزهاي مختلف سرو مي شود.
در اين حالت مي شود به جاي تخصيص دادن يک instance بخصوص براي قهوه در هر سفارش، نوع قهوه را از يک Pool استخراج کرد و تنها شماره ميز را کنارش نگه داشت.

public interface CoffeeOrder {   //Flyweight
   public void serveCoffee(int tableNo);
}


public class CoffeeFlavor implements CoffeeOrder {    //ConcreteFlyweight
   private String flavor;

   public CoffeeFlavor(String newFlavor) {
      flavor = newFlavor;
   }

   public String getFlavor() {
      return this.flavor;
   }

   public void serveCoffee(int tableNo) {
      System.out.println("Serving Coffee flavor "+flavor+ " to table number " + tableNo);
   }
}


public class CoffeeFlavorFactory {   //FlyweightFactory
   private Map flavors = new HashMap();

   public CoffeeFlavor getCoffeeFlavor(String flavorName) {
      CoffeeFlavor flavor = flavors.get(flavorName);
      if (flavor == null) {
         flavor = new CoffeeFlavor(flavorName);
         flavors.put(flavorName, flavor);
      }
      return flavor;
   }
}


public class TestFlyweight {

   private static CoffeeFlavor[] flavors = new CoffeeFlavor[100];

   private static int[] tables = new int[100];

   private static int ordersMade = 0;

   private static CoffeeFlavorFactory flavorFactory;

   public static void takeOrders(String flavorIn, int table) {
      flavors[ordersMade] = flavorFactory.getCoffeeFlavor(flavorIn);
      tables[ordersMade++] = table;
   }


   public static void main(String[] args) {
      flavorFactory = new CoffeeFlavorFactory();

      takeOrders("Cappuccino", 2);
      takeOrders("Cappuccino", 2);
      takeOrders("Xpresso", 1);
      takeOrders("Cappuccino", 97);
      takeOrders("Cappuccino", 97);
      takeOrders("Frappe", 3);
      takeOrders("Xpresso", 3);
      takeOrders("Cappuccino", 3);
      takeOrders("Frappe", 552);

      for (int i = 0; i < ordersMade; ++i) {
         flavors[i].serveCoffee(tables[i]);
      }
   }
}



Multitone Pattern
با توجه بيشتر به پياده سازي Flyweight Factory شباهت هايي بين آن و Singleton Pattern مي بينيم. کلاس هاي از اين دست را Multiton مي نامند. در Multiton نمونه ها بصورت زوج کليدهايي نگهداري مي شوند و بر اساس Key دريافت شده نمونه متناظر بازگردانده مي شود.

تفاوت Singleton و Multiton:
در Singleton تضمين مي شود که از کلاس مربوطه فقط يک نمونه در کل Application وجود دارد.
در Multiton Pattern تضمين مي‌شود که براي هر Key تنها يک Instance وجود دارد.



منبع ها:

  1. Design Patterns – Elements of Reusable Object Oriented Software (GOF) By Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides
  2. Flyweight in Wikipedia
  3. Flyweight in Devlake
  4. Multiton in Wikipedia

بازگشت به فهرست

Template Method

GOF Intent:
Define the skeleton of an algorithm in an operation, deferring some steps to subclasses. Template Method lets subclasses redefine certain steps of an algorithm without changing the algorithm's structure.

توسط Template Method Design Pattern مي توان، توالي کلي اجراي متدها را در کلاس پدر تعيين نمود و به Subclass ها امکان داد که ضمن تبعيت از روال تعيين شده در کلاس پدر، جزئيات هر مرحله را خود تعيين نمايند.

نمونه کد:
همه بازي ها مراحل مشخصي دارند از جمله تعيين تعداد بازيکن ها و شروع و پايان و در انتها معرفي برنده. اما قواعد هربازي با بازي ديگر متفاوت است.

abstract class Game {

   protected int playersCount;
   abstract void initializeGame();
   abstract void makePlay(int player);
   abstract boolean endOfGame();
   abstract void printWinner();

   // Template Method
   public final void playGame(int playersCount) {
      this.playersCount = playersCount;
      initializeGame();
      while (!endOfGame()) {
         makePlay(j);
      }
      printWinner();
   }
}


class Monopoly extends Game {

   void initializeGame() {
      // Initialize players
      // Initialize money
   }

   void makePlay(int player) {
      // Process one turn of player
   }

   boolean endOfGame() {
      // Return true if game is over according to Monopoly rules
   }

   void printWinner() {
      // Display who won
   }

   /* Specific declarations for the Monopoly game. */
   // ...
}


class Chess extends Game {

   void initializeGame() {
      // Initialize players
      // Put the pieces on the board
   }

   void makePlay(int player) {
      // Process a turn for the player
   }

   boolean endOfGame() {
      // Return true if in Checkmate or Stalemate has been reached
   }

   void printWinner() {
      // Display the winning player
   }

   /* Specific declarations for the chess game. */
   // ...
}



منبع ها:

  1. Design Patterns – Elements of Reusable Object Oriented Software (GOF) By Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides
  2. Template Method in Wikipedia

بازگشت به فهرست

Strategy

GOF Intent:
Define a family of algorithms, encapsulate each one, and make them interchangeable. Strategy lets the algorithm vary independently from clients that use it.

Strategy pattern همچنين با عنوان Policy Pattern شناخته مي شود.

چه وقت از Strategy استفاده کنيم؟ وقتي که...

نکته:
نکته قابل توجه در اين Pattern اين است که ما به جاي انجام Subclassing روي context الگوريتم (Strategy) مربوطه را از context جدا مي کنيم و آن را که يک interface است به صورتهايي که لازم است implement مي کنيم.


فايده هاي Strategy Pattern


نمونه کد:

interface Strategy {
   int execute(int a, int b);
}


class ConcreteStrategyAdd implements Strategy {
   public int execute(int a, int b) {
      System.out.println("Called ConcreteStrategyAdd's execute()");
      return a + b; // Do an addition with a and b
   }
}

class ConcreteStrategySubtract implements Strategy {
   public int execute(int a, int b) {
      System.out.println("Called ConcreteStrategySubtract's execute()");
      return a - b; // Do a subtraction with a and b
   }
}


class Context {
   private Strategy strategy;

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

   public int executeStrategy(int a, int b) {
      return strategy.execute(a, b);
   }
}


public static void main(String[] args) {
   Context context;
   context = new Context(new ConcreteStrategyAdd());
   int resultA = context.executeStrategy(3,4);
   context = new Context(new ConcreteStrategySubtract());
   int resultB = context.executeStrategy(3,4);
}

چند نمونه کاربرد:
نمونه 1

نمونه 2



منبع ها:

  1. Design Patterns – Elements of Reusable Object Oriented Software (GOF) By Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides
  2. Strategy Pattern in Wikipedia
  3. Strategy Pattern in Devlake

بازگشت به فهرست

Command

GOF Intent:
Encapsulate a request as an object, thereby letting you parameterize clients with different requests, queue or log requests, and support undoable operations.

اين Pattern همچنين به نامهاي Action يا Transaction نيز شناخته مي شود.

Command در واقع يک Object است که همه اطلاعات لازم براي اجراي يک درخواست (فرخواني يک متد) در آن Encapsulate شده است.

Command مستقل از کلاسي است که درخواست اجراي آن را کرده است (Client).

همچنين در اين Pattern جزئيات اجراي Commant از ديد Client پنهان است.

موارد استفاده Command Pattern:

1-پياده سازي Undo
مي توان Command هاي اجرا شده را در يک Stack قرار داد و در صورت لزوم و به درخواست کاربر Undo نمود.
2-رفتار Transactional
در مورد دستورات جداگانه اما وابسته که با Fail شدن يکي، کل عمليات بايد Rollback گردد. مشابه Undo بايد دستورات اجرا شده را در ليستي نگهداري کرد تا در زمان Rollback احتمالي مورد استفاده قرار گيرد.
3-پياده سازي Wizard
4-پياده سازي Progress Bar
5-فراخواني يک دستور به چند روش
مثلا در GUI ها که يک دستور ممکن است با کليک يک دکمه يا يک shortcut و غيره اجرا شود.

که در اين دياگرام...

Client: نمونه ConcreteCommand را ايجاد مي کند و receiver آن را مقداردهي مي‌نمايد.

Invoker: دستور اجراي Command را در زمان مناسب صادر مي کند. معمولا ليست Command ها را جهت اجرا نگهداري مي نمايد. بطور کلي وظيفه اش مديريت اجراي Command ها است.

Receiver: جزئيات پياده سازي اجراي Command در اين کلاس پياده شده است. هر Command بايد refernce به receiver مناسب خود را نگه دارد.


نمونه کد:

/*the Command interface*/
public interface Command {
   void execute();
}


/*the Invoker class*/
public class Switch {

   private List history = new ArrayList();

   public void storeAndExecute(Command cmd) {
      this.history.add(cmd); // optional
      cmd.execute();
   }
}


/*the Receiver class*/
public class Light {

   public void turnOn() {
      System.out.println("The light is on");
   }

   public void turnOff() {
      System.out.println("The light is off");
   }
}


/*the Command for turning on the light - ConcreteCommand #1*/
public class FlipUpCommand implements Command {

   private Light theLight;

   public FlipUpCommand(Light light) {
      this.theLight = light;
   }

   public void execute(){
      theLight.turnOn();
   }
}


/*the Command for turning off the light - ConcreteCommand #2*/
public class FlipDownCommand implements Command {

private Light theLight;

   public FlipDownCommand(Light light) {
      this.theLight = light;
   }

   public void execute() {
      theLight.turnOff();
   }
}


public static void main(String[] args){
   Light lamp = new Light();
   Command switchUp = new FlipUpCommand(lamp);
   Command switchDown = new FlipDownCommand(lamp);

   Switch s = new Switch();

   if (args[0].equalsIgnoreCase("ON")) {
      s.storeAndExecute(switchUp);
   } else {
      s.storeAndExecute(switchDown);
   }
}

در صورت لزوم مي توان Command ها را با Composite Pattern به هم متصل کرد.


تفاوت Command Design Pattern و Strategy Design Pattern:
Strategy الگوريتم هاي گوناگون اجراي يک کار بخصوص را درخود نگه مي دارد.
اما Command حاوي الگوريتم کار نيست، بلکه فراخواني يک دستور را درقالب يک Object در خود encapsulate مي‌کند. اين کار امکان مي‌دهد که دستور اجراي يک کار را بتوان بين Object هاي ديگر انتقال داد و از راه ها و در زمان هاي مختلف آن را اجرا نمود.



منبع ها:

  1. Design Patterns – Elements of Reusable Object Oriented Software (GOF) By Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides
  2. Command in Wikipedia
  3. Command in Devlake

بازگشت به فهرست

Memento

GOF Intent:
Without violating encapsulation, capture and externalize an object's internal state so that the object can be restored to this state later.

Memento Pattern براي نگهداري state يک Object جهت بازگرداندن آن به همان State از جمله هنگام Undo بکار مي رود.

کاربردهاي اين Pattern زياد است. هرجا که امکان Undo مورد نياز است يا در بازي ها که امکان بازگشت به مراحل قبل وجود دارد و غيره.

اين Pattern با عنوان Token نيز شناخته مي شود.

اين Pattern سه جزء دارد:

Caretaker هر زمان که لازم است از Originator درخواست يک memento مي کند که در آن State لحظه اي Originator نگهداري مي شود. Caretaker هنگام Undo دوباره memento را در اختيار Originator مي گذارد تا Originator با استفاده از آن خود را به آن State بازگرداند. همچنين caretaker نبايد بتواند هيچ تغييري روي memento ايجاد کند.

نمونه کد:

class Originator {
   private String state;

   public void set(String state) {
      System.out.println("Originator: Setting state to " + state);
      this.state = state;
   }

   public Memento saveToMemento() {
      System.out.println("Originator: Saving to Memento.");
      return new Memento(state);
   }

   public void restoreFromMemento(Memento memento) {
      state = memento.getSavedState();
      System.out.println("Originator: State restoring from Memento: " + state);
   }


   // Memento inner class
   public class Memento {
      private final String state;

   private Memento(String stateToSave) {
         state = stateToSave;
      }

      private String getSavedState() {
         return state;
      }
   }
}


class Caretaker {

   public static void main(String[] args) {
      List savedStates = new ArrayList();
      Originator originator = new Originator();

      originator.set("State1");
      originator.set("State2");
      savedStates.add(originator.saveToMemento());
      originator.set("State3");
      savedStates.add(originator.saveToMemento());
      originator.set("State4");

      originator.restoreFromMemento(savedStates.get(1));
   }
}

نکته:
در اين مثال state يک String ساده در نظر گرفته شده است. اما در حالت واقعي State اغلب يک Object است. در اين حالت براي ساخت memento از دستور Clone استفاده مي شود.

public class Memento {
   private final State state;

   private Memento(State stateToSave) {
      state = stateToSave.clone();
   }

   private Sate getSavedState() {
      return state;
   }
}

چند نکته:

  1. درصورتي که نياز باشد حجم اطلاعات زيادي در Memento نگهداري شود يا Caretaker به دفعات زياد memento را دريافت کند، اين کار مي تواند overhead زيادي روي سيستم داشته باشد و منابع سيستم را اشغال کند. يک راه حل براي اين مشکل اين است که تغييرات در memento بصورت incremental نگهداري شوند. يعني به جاي نگهداري کل state، فقط تغييرات انجام شده نسبت به State قبل نگهداري شود. اين کار البته مديريت حساب شده تري در ذخيره و بازيابي memento را مي طلبد.
  2. بايد تمام تمهيدات ممکن را انديشيد که حتي المقدور تنها Originator امکان تغيير memento را داشته باشد.
  3. وظيفه پاک کردن memento هايي که مورد استفاده قرار مي گيرند با Caretaker است. هرچند Caretaker اصولا ديدي از حجم data و state موجود در memento ندارد. با اين حال بي توجهي به پاک کردن memento ممکن است، حافظه زيادي را اشغال و بلا استفاده کند.



منبع ها:

  1. Design Patterns – Elements of Reusable Object Oriented Software (GOF) By Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides
  2. Memento Pattern in Wikipedia

بازگشت به فهرست

Mediator

GOF Intent:
Define an objcet that encapsulates how a set of objects interact. Mediator promote loose coupling by keeping objects from referring to each other explicitly, and it lets you vary their interaction independently.

Mediator يک Object است که ارتباط بين Object هاي ديگر را مديريت مي‌کند. به جاي اينکه Object هاي مرتبط مستقيما با هم تعامل داشته باشند، با واسطه Mediator صحبت مي کنند.

فلسفه وجود Mediator اين است که نحوه Communication ديگر Object ها را در خود encapsulate کند.

فوايد Mediator:


اجزاي تشکيل دهنده Mediator Pattern:
Colleague:
در اين Pettern به Object هايي که قرار است با هم ارتباط داشته باشند، Colleague گفته مي شود.
Mediator:
Object اي که پياده سازي نحوه ارتباط در آن انجام مي شود.

Mediator

متدهايي که لازم است در اين Pattern پياده شوند بدين صورت است:

Mediator

کاربرد هاي Mediator Pattern:
Mediator Pattern دو تيپ کاربرد اصلي دارد

  1. براي تبادل پيغام بين Object هاي مشابه بر اساس گروه بندي و غيره. مانند شبکه هاي اجتماعي که کاربران بر اساس گروه بنديهاي موجود به کاربران گروه دلخواه پيام مي فرستند يا چيزي را share مي کنند.
  2. براي ايجاد هماهنگي بين Object ها در وضعيت هاي مختلف. فرض کنيد يک GUI داريم که اجزاي آن با انتخاب يکي يا بقيه به شکل هاي مختلف Disable يا Enable مي شوند. مي توان مديريت اين کار را در Mediator انجام داد.

نمونه کد:

public interface IColleague {
   public void receiveMessage(String msg);
   public void sendMessage(Mediator aMediator, String msg);
}


public abstract class Mediator {
   protected ArrayList<IColleague> listColleagues = new
             ArrayList<IColleague>();

   public abstract void distributeMessage(IColleague sender, String msg);
   public abstract void registerColleague(IColleague aColleague);
}


public class ConcreteColleague implements IColleague {
   private String name;

   public ConcreteColleague(String name){
      this.name = name;
   }

   public void receiveMessage(String msg) {
      System.out.println(name+" Received message: "+msg);
   }

   public void sendMessage(Mediator aMediator, String msg) {
      aMediator.distributeMessage(this, msg);
   }
}


public class ConcreteMediator extends Mediator {

   public void distributeMessage(IColleague sender, String msg){
      for (IColleague aColleague : listColleagues){
         if (aColleague != sender)
            aColleague.receiveMessage(msg);
      }
   }

   public void registerColleague(IColleague aColleague){
      listColleagues.add(aColleague);
   }

}


public static void main(String[] args){
   IColleague c1 = new ConcreteColleague("Colleague1");
   IColleague c2 = new ConcreteColleague("Colleague2");
   IColleague c3 = new ConcreteColleague("Colleague3");

   Mediator m1 = new ConcreteMediator();
   Mediator m2 = new ConcreteMediator();

   m1.registerColleague(c1);
   m1.registerColleague(c2);
   c1.sendMessage(m1, "message from c1 to m1 group");

   m2.registerColleague(c2);
   m2.registerColleague(c3);
   c1.sendMessage(m2, "message from c1 to m2 group");
}

خروجي:

run:
Colleague2 Received message: message from c1 to m1 group
Colleague2 Received message: message from c1 to m2 group
Colleague3 Received message: message from c1 to m2 group

مثال 2:
به جاي ارسال و دريافت پيام مي توان از Mediator براي نظم بخشيدن به وضعيت Object ها در ارتباط با هم استفاده کرد.

در اين مثال با انتخاب از combobox يا فشردن يک Button يا تيک زدن يک Checkbox وضعيت اجزاي صفحه تغيير مي کند.

public interface Mediator{

   public void gotoState1();
   public void gotoState2();
   public void gotoState3();
}


public class ConcreteMediator implements Mediator{

   Button btn1;
   Button btn2;
   CheckBox check1;
   CheckBox chech2;
   ComboBox combo1;
   Label label1;
   ProgressBar = progress1;

   public void registerBtn1(Button btn1){
      this.btn1 = btn1;
   }
   //Other register methods
   ...

   public void gotoState1(){
      btn1.enabled = false;
      btn2.enabled = true;
      Progress1.doProgress();
      Label1.value = "State 1";
      ....
   }

   public void gotoState2(){
      ....
   }
   ....
}


public interface GUIElement{
   public void execute();
}


public class Button extends GUIButton implements GUIElement{

   Mediator m;

   public void execute(){
      m.gotoState1();
   }
}

// other Colleagues implementation:

فايده هاي Mediator Pattern:

  1. کاهش Subclassing: اگر نحوه ارتباط در خود Object پياده شده بود، براي تغيير آن بايد Object جديدي Extends مي شد. اما در اين حالت فقط Mediator در صورت لزوم Extends مي شود.
  2. کاهش Coupling بين Object ها
  3. با وجود Mediator همه Objectها تنها با Mediator ارتباط دارند. بنابراين ارتباط چند به چند با وجود Mediator تبديل به يک به چند مي شود و اين باعث ساده تر شدن و قابل فهم تر شدن برنامه مي شود.
  4. با وجود Mediator تعامل و Communication بين Object ها از خودشان مستقل مي شود و بدين ترتيب بطور مجزا مي توان روي آن تمرکز نمود.
  5. با وجود Mediator کنترل ارتباط بين Object ها در يک جا و بطور متمرکز انجام مي شود.



منبع ها:

  1. Design Patterns – Elements of Reusable Object Oriented Software (GOF) By Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides
  2. Mediator in Wikipedia
  3. Mediator in Devlake

بازگشت به فهرست

State Design Pattern

GOF Intent:
Allow an object to alter its behavior when its internal state changes. The object will appear to change its class;

در اين Pattern امکان اين را فراهم مي کند که رفتار، يا State يک Object را در زمان اجرا تغيير دهيم. در اينجا هر رفتار در يک کلاس State نگهداري مي شود و با تغيير آن مي توان رفتار يا state کلاس را تغيير داد.


اين Pattern شباهت زيادي با Strategy Pattern دارد.

نمونه کد:

فرض کنيد در يک بازي کامپيوتري يک جنگجو يا Warrior داريم که پس از جنگيدن حين اجرا، توان او بين Strong و Normal و Weak تغيير مي کند:

public interface IHealth {
   public void doBattle(Warrior w);
   public void showHealth();
}


public class Normal implements IHealth{

   public void doBattle(Warrior w) {
      w.setHealth(new Weak());
   }

   public void showHealth() {
      System.out.println("Warrior is Normal");
   }
}


public class Strong implements IHealth{
   public void doBattle(Warrior w) {
      w.setHealth(new Normal());
   }

   public void showHealth() {
      System.out.println("Warrior is Strong");
   }
}


public class Weak implements IHealth{
   public void doBattle(Warrior w) {
      w.setHealth(new Strong());
   }

   public void showHealth() {
      System.out.println("Warrior is Weak");
   }
}


public class Warrior {
   private IHealth health = new Strong();

   public void setHealth(IHealth h){
      health = h;
   }

   public void showHealth(){
      health.showHealth();
   }

   public void battle(){
      health.doBattle(this);
   }
}


public static void main(String[] args) {
   Warrior w = new Warrior();
      w.showHealth();
      w.battle();
      w.showHealth();
      w.battle();
      w.showHealth();
}

نکته:
State Object ها معمولا Singleton هستند.



منبع ها:

  1. Design Patterns – Elements of Reusable Object Oriented Software (GOF) By Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides
  2. State in Devlake
  3. State in Wikipedia

بازگشت به فهرست

Observer

GOF Intent:
Define a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically.

اين Pattern با نام هاي Dependents و Publish-Subscribe نيز شناخته مي شوند.

وقتي از اين Pattern استفاده مي شود که با تغيير در يک Object، لازم است تعداد ديگري از Object ها هم تغيير کنند يا از تغيير Object اول مطلع گردند.

اين Pattern يکي از اجزاي کليدي Pattern معماري MVC است.

ساختار اين Pattern بدين صورت است:

نمونه کد:

public interface Subject {
   public void register(Observer o);
   public void unregister(Observer o);
   public void notifyObservers();
}


public interface Observer {
   public void update(int state);
}


public class ConcreteSubject implements Subject{
   ArrayList<Observer> list = new ArrayList<Observer>();
   int subjectState = 0;

   public void setState(int state){
      subjectState = state;
   }

   public void register(Observer o) {
      list.add(o);
   }

   public void unregister(Observer o) {
      list.remove(o);
   }

   public void notifyObservers() {
      for (Observer o :list){
         o.update(subjectState);
      }
   }
}


public class ConcreteObserver implements Observer{
   int observerState;
   Subject theSubject;

   public ConcreteObserver(Subject theSubject){
      this.theSubject = theSubject;
      theSubject.register(this);
   }

   public void update(int state) {
      observerState = state;
   }

   public void display(){
      System.out.println("Current State is "+observerState);
   }
}


public class ConcreteObserver2 implements Observer{
   int observerState;

   public void update(int state) {
      observerState = state;
   }

   public void display(){
      System.out.println("Current State is "+observerState);
   }
}


public static void main(String[] args){
   ConcreteSubject aSubject = new ConcreteSubject();

   ConcreteObserver observer1 = new ConcreteObserver(aSubject);

   ConcreteObserver2 observer2 = new ConcreteObserver2();
   aSubject.register(observer2);

   aSubject.setState(7);
   aSubject.notifyObservers();

   observer1.display();
   observer2.display();
}

بسياري از زبانهاي برنامه نويسي ، پياده سازي هايي از کلاس هاي Observer Pattern ارائه مي دهند. از جمله در جاوا

java.util.Observer
java.util.Observable

نمونه کد استفاده از کلاس هاي جاوا:

/****************************
Observable Class Implementation
****************************/
import java.util.Observable;

public class EventSource extends Observable{
   private String resp = "";

   public void setResp(String resp){
      this.resp = resp;
      setChanged();
      notifyObservers(resp);
   }
}


/****************************
Observer Class Implementation
****************************/
import java.util.Observable;
import java.util.Observer;

public class ResponseHandler implements Observer {
   private String resp;

   public void update(Observable o, Object arg) {
      resp = (String)arg;
   }

   public void displayResp(){
      System.out.println("Resp : "+resp);
   }
}


/****************************
Running Method
****************************/
public static void main(String[] args){
   EventSource es = new EventSource();
   ResponseHandler rh1 = new ResponseHandler();

   es.addObserver(rh1);

   es.setResp("Hello to All Observers!");

   rh1.displayResp();
}

تفاوت Mediator Pattern و Observer Pattern
اين دو Pattern از نظر شکل و نحوه پياده سازي شباهت زيادي با هم دارند. اما آنچه که اين دو را از هم متمايز مي کند اين است که...
در Mediator Pattern، همه Object ها مي توانند نقش Sender و Receiver را ايفا کنند. فقط ارسال و دريافت با مديريت يک Object جداگانه به نام mediator صورت مي گيرد
اما در Observer Pattern، نقش Sender و Receiver کاملا مشخص و از هم تفکيک شده است. در اين Pattern هميشه يک فرستنده (Subject) و چندين دريافت کننده (Observer) داريم.



منبع ها:

  1. Design Patterns – Elements of Reusable Object Oriented Software (GOF) By Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides
  2. Observer in Wikipedia
  3. Observer in Codeproject
  4. Observer in Devlake

بازگشت به فهرست

Iterator

GOF Intent:
Provide a way to access the elements of an aggregate object sequentially without exposing its underlying representation.

اين Pattern به نام Cursor نيز خوانده مي شود.

گاهي در برنامه با ليست هاي گوناگوني از Object هاي مختلف سر و کار داشته باشيم. هر کدام از اين ليست ها به گونه خاصي پياده سازي مي شوند و جزئيات هريک با ديگري متفاوت است.

در اين شرايط اگر Iterator Pattern استفاده شود نتيجه اش اين است که کاربر، براي پيمايش اين ليست ها، بدونه اينکه درگير جزئيات شود، از يک سري متدهاي از پيش تعيين شده براي عملياتي همچون next، Previous و غيره روي همه ليست ها استفاده مي‌کند.

در ساختار بالا Aggregate کلاسي است که ليست اصلي را در خود نگه مي دارد و بايد مورد پيمايش قرار گيرد.

نمونه کد:

public interface Iterator {
   public void first();
   public void next();
   public boolean hasNext(); // isDone
   public String currentItem();
}


public interface Aggregate {
   public Iterator CreateIterator();
   public void addItem(String item);
}


public class ConcreteIterator implements Iterator{
   private ArrayList<String> list;
   private int currentPosition = 0;

   public ConcreteIterator(ArrayList<String> list){
      this.list = list;
   }

   public void first() {
      currentPosition = 0;
   }

   public void next() {
      if (currentPosition < list.size())
         currentPosition++;
   }

   public boolean hasNext() {
      if (currentPosition < list.size())
         return true;
      else
         return false;
   }

   public String currentItem() {
      return list.get(currentPosition);
   }
}


public class ConcreteAggregate implements Aggregate{

   private ArrayList<String> aggregatelist = new ArrayList<String>();

   public Iterator CreateIterator() {
      return new ConcreteIterator(aggregatelist);
   }

   public void addItem(String item) {
      aggregatelist.add(item);
   }
}


public static void run(){

   Aggregate a = new ConcreteAggregate();

   a.addItem("Item1");
   a.addItem("Item2");
   a.addItem("Item3");
   a.addItem("Item4");
   a.addItem("Item5");

   Iterator i = a.CreateIterator();

   while (i.hasNext()){
      System.out.println(i.currentItem());
      i.next();
   }
}

برخي از زبانها تمهيداتي براي اين Pattern در نظر گرفته اند. از جمله در جاوا interface پيش فرضي براي iterator داريم که در صورت لزوم مي توان آن را implement کرد.
java.util.Iterator

اين interface اي است که signature متدهاي زير در آن وجود دارد:
hasNext()
next()
remove()


برخي از ليست هاي موجود در جاوا از جمله ArrayList، کلاس Iterator خود را پياده سازي کرده اند.

مثال:

ArrayList<String> list = new ArrayList<String>();

list.add("item1");
list.add("item2");
list.add("item3");

for (Iterator<String> it = list.iterator(); it.hasNext();) {
   System.out.println(it.next());
}



منبع ها:

  1. Design Patterns – Elements of Reusable Object Oriented Software (GOF) By Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides
  2. Design Patterns for dummies, By Steve Holzner
  3. Iterator in Devlake

بازگشت به فهرست

Chain Of Responsibility

GOF Intent:
Avoid coupling the sender of a request to its receiver by giving more than one object a chance to handle the request. Chain the receiving objects and pass the request along the chain until an object handles it.

در اين Pattern درخواست به يک Chain سپرده مي شود و بين اجزاي زنجيره دست به دست مي شود تا بالاخره يکي بتواند درخواست را Process نمايد.

نمونه کد:

public class LoanRequest {
   int amount;

   public LoanRequest(int amount) {
      this.amount = amount;
   }

   public int getAmount(){
      return amount;
   }
}


public abstract class Approver {

   protected Approver successor;

   public void setSuccessor(Approver successor){
      this.successor = successor;
   }

   abstract public void processRequest(LoanRequest request);
}


public class Employee extends Approver{

   public void processRequest(LoanRequest request) {
      if (request.getAmount() <= 1000)
         System.out.println("Request Approved by Employee!");
      else if (successor != null)
         successor.processRequest(request);
   }
}

public class Manager extends Approver{

   public void processRequest(LoanRequest request) {
      if (request.getAmount() <= 2000)
         System.out.println("Request Approved by Manager!");
      else if (successor != null)
         successor.processRequest(request);
   }
}


public class Director extends Approver{

   public void processRequest(LoanRequest request) {
      if (request.getAmount() <= 3000)
         System.out.println("Request Approved by Director!");
      else if (successor != null)
         successor.processRequest(request);
   }
}


public static void main(String[] args){

   Approver a = new Employee();
   Approver b = new Manager();
   Approver c = new Director();

   a.setSuccessor(b);
   b.setSuccessor(c);

   a.processRequest(new LoanRequest(500));
   a.processRequest(new LoanRequest(1500));
   a.processRequest(new LoanRequest(2500));
}

در مثال بالا درخواست هاي وام بسته به مبلغ شان توسط يکي از اجزاي زنجيره تاييد مي گردند.

فايده هاي Chain of Responsibility:

نکته:
تضميني نيست که درخواست حتما انجام شود. چون request دريافت کننده مشخص و اختصاصي ندارد. درصورتي که Chain به درستي سازماندهي نشده باشد، ممکن است درخواست اصلا انجام نشود.



منبع ها:

  1. Design Patterns – Elements of Reusable Object Oriented Software (GOF) By Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides
  2. Chain of Responsibility in Devlake
  3. Chain of Responsibility in Wikipedia

بازگشت به فهرست

Interpreter

GOF Intent:
Given a language, define a representation for its grammar along with an interpreter that uses the representation to interpret sentences in the language.

از Interpreter Pattern زماني استفاده مي‌شود که بخواهيم يک regular Expression يا يک Pattern رشته اي را تفسير نماييم.

اين Pattern بر خلاف ساير Pattern ها استفاده عمومي ندارد و فقط در موارد خاص (تفسير Expression) مورد استفاده قرار مي گيرد.

براي درک بهتر Interpreter Pattern مثالي را پي مي گيريم.

مثال:
عبارت هاي محاسباتي با دو عملگر جمع و تفريق را در نظر بگيريد:

(10 + 7) – (6 – 1) + 5

در اين مثال گرامر اينگونه خواهد بود:

Expression = NumberExpression | AddExpression | SubtractExpression
AddExpression = Expression + Expression
SubtractExpression = Expression - Expression

براي محاسبه عبارت مي توان آن را به حالت Postfix تبديل کرد و در قالب درخت سازماندهي نمود:

(10 + 7) – (6 – 1) + 5 -----> 10 7 + 6 1 - - 5 +

کارهايي که در اين مثال انجام شد مقدمات پياده سازي Interpreter Pattern است.

مراحل اعمال Interpreter Design Pattern :

  1. براي زبان گرامر تعريف کنيد.
  2. هر يک از بخش هاي گرامر را به يک کلاس map کنيد.
  3. کلاس ها را در ساختاري مشابه Composite Pattern سازماندهي نماييد.
  4. متد interpret(Context) را پياده سازي نماييد.

اجزاي Interpreter Design Pattern:
1- AbstractExpression : يک متد با نام Intepret تعريف مي کند
2- TerminalExpression : کلاس متناظر براي اجزاي با مقدار مشخص از جمله اعداد. در ساختار درختي Composite اين TerminalExpression ها برگ هاي درخت هستند.
3- NonterminalExpression : کلاس متناظر براي هر يک از عملگرها يا قواعد زبان. درساختار Composite Pattern، اين ها کلاس هاي Composite هستند.
4- Context : کلاسي است که درصورت لزوم عبارت اصلي و عبارت Interpret شده را در خود Encapsulate مي کند و بصورت Global در دسترس متد Interpret قرار دارد. در مثال ما يک String ساده است.
5- Client: دو وظيفه دارد

نمونه کد:

public interface Expression {
   public int interpret();
}


public class NumberExpression implements Expression{
   int number;

   public NumberExpression(int number){
      this.number = number;
   }

   public int interpret() {
      return number;
   }
}


public class AddExpression implements Expression{

   Expression leftExpression;
   Expression rightExpression;

   public AddExpression(Expression left, Expression right){
      leftExpression = left;
      rightExpression = right;
   }

   public int interpret() {
      return leftExpression.interpret() + rightExpression.interpret();
   }
}


public class SubtractExpression implements Expression{

   Expression leftExpression;
   Expression rightExpression;

   public SubtractExpression(Expression left, Expression right){
      leftExpression = left;
      rightExpression = right;
   }

   public int interpret() {
      return leftExpression.interpret() - rightExpression.interpret();
   }
}


import java.util.Stack;
import java.util.ArrayList;

public class Evaluator {
   private Expression syntaxTree;

   public Evaluator(String expression){

      String[] tokens = expression.split(" ");
      Stack<Expression> expressionStack = new Stack<Expression>();

      for (String token : tokens){
         if (! (token.equals("+") || token.equals("-"))){
            expressionStack.push(new NumberExpression(Integer.parseInt(token)));
         } else {

            Expression right = expressionStack.pop();
            Expression left = expressionStack.pop();

            if (token.equals("+"))
               expressionStack.push(new AddExpression(left, right));
            else
               expressionStack.push(new SubtractExpression(left, right));
         }
      }
      syntaxTree = expressionStack.pop();
   }

   public int interpret(){
      return syntaxTree.interpret();
   }
}


public static void main(String[] args){
   // (10 + 6) - (7 - 2)
   String context = "10 6 + 7 2 - -";
   Evaluator e1 = new Evaluator(context);
   System.out.println(e1.interpret());
}

نکته 1:
با استفاده از Interpreter Pattern تغيير و گسترش گرامر ساده است. زيرا هر قاعده گرامر در يک کلاس پياده شده است و با تغيير آنها يا Extend نمون آنها مي توان گرامر را تغيير داد.

نکته 2:
اما استفاده از Interpreter Pattern براي گرامر هاي پيچيده و حجيم، مديريت و Maintanance برنامه را دشوار مي کند.



منبع ها:

  1. Design Patterns – Elements of Reusable Object Oriented Software (GOF) By Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides
  2. Interpreter in Devlake
  3. Interpreter in Wikipedia
  4. Interpreter in Sourcemaking

بازگشت به فهرست

Visitor Design Pattern

GOF Intent:
Represent an operation to be performed on the elements of an object structure. Visitor lets you define a new operation without changing the classes of the elements on which it operates.

هدف Visitor Pattern جدايي "ساختمان داده" از "منطق عملياتي" است.

براي درک بهتر اين Pattern مثالي را پي مي گيريم.

مثال:
در يک سيستم بانکي انواع حساب وجود دارد. از جمله حساب جاري، سپرده و غيره. روي هريک از اين انواع حساب بعضي عمليات از جمله واريز و برداشت، مانده گيري، محاسبه سود و غيره انجام مي شود، اما در مورد هر حساب به روش مخصوص به خود.

اگر در اين مثال Visitor Pattern را پياده کنيم چنين طراحي خواهيم داشت:

در حالت کلي ساختار Visitor Pattern بدين صورت است:

در ساختار فوق، Object Structure ساختار نگهدارنده عناصر Element است. ميتواند آرايه، ليست، درخت و غيره باشد.

نمونه کد:

public interface Element {
   public void accept(Visitor v);
   public String getName();
}


public class ConcreteElementA implements Element{
   private String name;

   public ConcreteElementA(String name){
      this.name = name;
   }

   public String getName(){
      return name;
   }

   public void accept(Visitor v) {
      v.visitConcreteElementA(this);
   }
}


public class ConcreteElementB implements Element{
   private String name;

   public ConcreteElementB(String name){
      this.name = name;
   }

   public String getName(){
      return name;
   }

   public void accept(Visitor v) {
      v.visitConcreteElementB(this);
   }

}


public interface Visitor {
   public void visitConcreteElementA(Element e);
   public void visitConcreteElementB(Element e);
}


public class ConcreteVisitorOperation1 implements Visitor{

   public void visitConcreteElementA(Element e) {
      System.out.println("Operation1 for Element A run for "+e.getName());
   }

   public void visitConcreteElementB(Element e) {
      System.out.println("Operation1 for Element B run for "+e.getName());
   }
}


public class ConcreteVisitorOperation2 implements Visitor{

   public void visitConcreteElementA(Element e) {
      System.out.println("Operation2 for Element A run for "+e.getName());
   }

   public void visitConcreteElementB(Element e) {
      System.out.println("Operation2 for Element B run for "+e.getName());
   }
}


public static void main(String[] args){
   ArrayList<Element> elementList = new ArrayList<Element>();
   elementList.add(new ConcreteElementA("ElementA1"));
   elementList.add(new ConcreteElementA("ElementA2"));
   elementList.add(new ConcreteElementB("ElementB1"));
   elementList.add(new ConcreteElementB("ElementB2"));

   Visitor v1 = new ConcreteVisitorOperation1();
   for (Element e : elementList){
      e.accept(v1);
   }
}

فايده هاي Visitor Pattern:

عيب هاي Visitor Pattern:
نتيجه:
بطور کلي از Visitor Pattern، زماني استفاده کنيد که کلاس هاي اصلي به ندرت تغيير مي کنند، اما لازم است در طول زمان Operator هاي جديدي به آنها اضافه شود. اگر خود کلاس تغييراتش زياد است، بهتر است از اين Pattern استفاده نشود و Operator ها در خود کلاس پياده شوند.



منبع ها:

  1. Design Patterns – Elements of Reusable Object Oriented Software (GOF) By Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides
  2. Visitor in Devlake

بازگشت به فهرست