設計模式系列 - 行爲型模式(下)

行爲設計模式是識別對象之間的通訊模式,行爲模式涉及對象之間的責任分配,或者,將行爲封裝在對象中並將請求委託給它,也就是對象之間的關係。java

涉及:
* 狀態模式
中介模式
* 觀察者模式
備忘錄模式
迭代器模式
命令模式
* 策略模式
* 模板模式
* 訪客模式示例
責任鏈模式linux

觀察者模式

根據GoF定義,observer模式定義了對象之間的一對多依賴關係,當一個對象改變狀態時,它的全部依賴關係都會被自動通知和更新。它也被稱爲發佈-訂閱模式。
在觀察者模式中,有許多觀察者(訂閱者對象)正在觀察特定的主題(發佈者對象)。觀察者向一個主題註冊,以便在該主題內部發生更改時得到通知。
觀察者對象能夠在任什麼時候間點註冊或從主體註銷。它有助於使對象對象鬆散耦合。
關鍵代碼:在抽象類裏有一個 ArrayList 存放觀察者們算法

Subject.java
public interface Subject 
{
    public void attach(Observer o);
    public void detach(Observer o);
    public void notifyUpdate(Message m);
}

MessagePublisher.java
import java.util.ArrayList;
import java.util.List;
 
public class MessagePublisher implements Subject {
     
    private List<Observer> observers = new ArrayList<>();
 
    @Override
    public void attach(Observer o) {
        observers.add(o);
    }
 
    @Override
    public void detach(Observer o) {
        observers.remove(o);
    }
 
    @Override
    public void notifyUpdate(Message m) {
        for(Observer o: observers) {
            o.update(m);
        }
    }
}
//Observer and ConcreteObservers
Observer.java
public interface Observer 
{
    public void update(Message m);
}
MessageSubscriberOne.java
public class MessageSubscriberOne implements Observer 
{
    @Override
    public void update(Message m) {
        System.out.println("MessageSubscriberOne :: " + m.getMessageContent());
    }
}
MessageSubscriberTwo.java
public class MessageSubscriberTwo implements Observer 
{
    @Override
    public void update(Message m) {
        System.out.println("MessageSubscriberTwo :: " + m.getMessageContent());
    }
}
MessageSubscriberThree.java
public class MessageSubscriberThree implements Observer 
{
    @Override
    public void update(Message m) {
        System.out.println("MessageSubscriberThree :: " + m.getMessageContent());
    }
}
//State object
//This must be an immutable object so that no class can modify it’s content by mistake.

Message.java
public class Message 
{
    final String messageContent;
     
    public Message (String m) {
        this.messageContent = m;
    }
 
    public String getMessageContent() {
        return messageContent;
    }
}
//Now test the communication between publisher and subscribers.
Main.java
public class Main 
{
    public static void main(String[] args) 
    {
        MessageSubscriberOne s1 = new MessageSubscriberOne();
        MessageSubscriberTwo s2 = new MessageSubscriberTwo();
        MessageSubscriberThree s3 = new MessageSubscriberThree();
         
        MessagePublisher p = new MessagePublisher();
         
        p.attach(s1);
        p.attach(s2);
         
        p.notifyUpdate(new Message("First Message"));   //s1 and s2 will receive the update
         
        p.detach(s1);
        p.attach(s3);
         
        p.notifyUpdate(new Message("Second Message")); //s2 and s3 will receive the update
    }
}
Program output:
MessageSubscriberOne :: First Message
MessageSubscriberTwo :: First Message
MessageSubscriberTwo :: Second Message
MessageSubscriberThree :: Second Message


狀態模式

根據GoF的定義,狀態容許對象在其內部狀態改變時改變其行爲。對象的每一個可能狀態都應有一個單獨的具體類。每一個具體的狀態對象都有接受或拒絕狀態轉換請求的邏輯,該請求基於其當前狀態和做爲方法參數傳遞給它的上下文信息。
當咱們處理的對象在其生命週期中處於不一樣的狀態,以及它如何基於其當前狀態處理傳入請求(或進行狀態轉換)時,咱們均可以使用狀態模式。
若是咱們在這種狀況下不使用狀態模式,咱們最終會獲得不少If-else語句,這些語句會使代碼基變得難看、沒必要要的複雜和難以維護。編程

PackageState.java
public interface PackageState 
{
    public void updateState(DeliveryContext ctx);
}
Acknowledged.java
public class Acknowledged implements PackageState 
{
    //Singleton
    private static Acknowledged instance = new Acknowledged();
 
    private Acknowledged() {}
 
    public static Acknowledged instance() {
        return instance;
    }
     
    //Business logic and state transition
    @Override
    public void updateState(DeliveryContext ctx) 
    {
        System.out.println("Package is acknowledged !!");
        ctx.setCurrentState(Shipped.instance());
    }
}
Shipped.java
public class Shipped implements PackageState 
{
    //Singleton
    private static Shipped instance = new Shipped();
 
    private Shipped() {}
 
    public static Shipped instance() {
        return instance;
    }
     
    //Business logic and state transition
    @Override
    public void updateState(DeliveryContext ctx) 
    {
        System.out.println("Package is shipped !!");
        ctx.setCurrentState(InTransition.instance());
    }
}
InTransition.java
public class InTransition implements PackageState 
{
    //Singleton
    private static InTransition instance = new InTransition();
 
    private InTransition() {}
 
    public static InTransition instance() {
        return instance;
    }
     
    //Business logic and state transition
    @Override
    public void updateState(DeliveryContext ctx) 
    {
        System.out.println("Package is in transition !!");
        ctx.setCurrentState(OutForDelivery.instance());
    }
}
OutForDelivery.java
public class OutForDelivery implements PackageState 
{
    //Singleton
    private static OutForDelivery instance = new OutForDelivery();
 
    private OutForDelivery() {}
 
    public static OutForDelivery instance() {
        return instance;
    }
     
    //Business logic and state transition
    @Override
    public void updateState(DeliveryContext ctx) 
    {
        System.out.println("Package is out of delivery !!");
        ctx.setCurrentState(Delivered.instance());
    }
}
Delivered.java
public class Delivered implements PackageState 
{
    //Singleton
    private static Deliveredinstance = new Delivered();
 
    private Delivered() {}
 
    public static Deliveredinstance() {
        return instance;
    }
     
    //Business logic
    @Override
    public void updateState(DeliveryContext ctx) 
    {
        System.out.println("Package is delivered!!");
    }
}

DeliveryContext.java
public class DeliveryContext {
     
    private PackageState currentState;
    private String packageId;
     
    public DeliveryContext(PackageState currentState, String packageId) 
    {
        super();
        this.currentState = currentState;
        this.packageId = packageId;
         
        if(currentState == null) {
            this.currentState = Acknowledged.instance();
        }
    }
 
    public PackageState getCurrentState() {
        return currentState;
    }
 
    public void setCurrentState(PackageState currentState) {
        this.currentState = currentState;
    }
     
    public String getPackageId() {
        return packageId;
    }
 
    public void setPackageId(String packageId) {
        this.packageId = packageId;
    }
 
    public void update() {
        currentState.updateState(this);
    }
}
Now test the code.

Main.java
public class Main 
{
    public static void main(String[] args) 
    {
        DeliveryContext ctx = new DeliveryContext(null, "Test123");
         
        ctx.update();
        ctx.update();
        ctx.update();
        ctx.update();
        ctx.update();
    }
}
Program Output:

Package is acknowledged !!
Package is shipped !!
Package is in transition !!
Package is out of delivery !!
Package is delivered !!

 

策略模式

咱們在運行時從多個其餘實現中爲同一任務選擇算法或任務的特定實現。重要的一點是,這些實現是可互換的——基於任務,能夠在不干擾應用程序工做流的狀況下選擇實現。
策略模式包括從其宿主類中移除算法並將其放在單獨的類中,以便在相同的編程上下文中可能有不一樣的算法(即策略),這些算法能夠在運行時選擇。
策略模式容許客戶機代碼從一系列相關但不一樣的算法中進行選擇,並提供了一種在運行時根據客戶機上下文選擇任何算法的簡單方法。設計模式

關鍵點:典型的面向接口編程。ide

ISocialMediaStrategy.java

public interface ISocialMediaStrategy 
{
    public void connectTo(String friendName);
}
SocialMediaContext.java

public class SocialMediaContext 
{
    ISocialMediaStrategy smStrategy;
 
    public void setSocialmediaStrategy(ISocialMediaStrategy smStrategy) 
    {
        this.smStrategy = smStrategy;
    }
 
    public void connect(String name) 
    {
        smStrategy.connectTo(name);
    }
}
FacebookStrategy.java
public class FacebookStrategy implements ISocialMediaStrategy {
 
    public void connectTo(String friendName) 
    {
        System.out.println("Connecting with " + friendName + " through Facebook");
    }
}
GooglePlusStrategy.java

public class GooglePlusStrategy implements ISocialMediaStrategy {
 
    public void connectTo(String friendName) 
    {
        System.out.println("Connecting with " + friendName + " through GooglePlus");
    }
}
TwitterStrategy.java
public class TwitterStrategy implements ISocialMediaStrategy {
 
    public void connectTo(String friendName) 
    {
        System.out.println("Connecting with " + friendName + " through Twitter");
    }
}
OrkutStrategy.java
public class OrkutStrategy implements ISocialMediaStrategy {
 
    public void connectTo(String friendName) 
    {
        System.out.println("Connecting with " + friendName + " through Orkut [not possible though :)]");
    }
}

//Demo
//Now see how these strategies can be used in runtime. 
public class Demo {
    public static void main(String[] args) {
 
        // Creating social Media Connect Object for connecting with friend by
        // any social media strategy.
        SocialMediaContext context = new SocialMediaContext();
 
        // Setting Facebook strategy.
        context.setSocialmediaStrategy(new FacebookStrategy());
        context.connect("Lokesh");
 
        System.out.println("====================");
 
        // Setting Twitter strategy.
        context.setSocialmediaStrategy(new TwitterStrategy());
        context.connect("Lokesh");
 
        System.out.println("====================");
 
        // Setting GooglePlus strategy.
        context.setSocialmediaStrategy(new GooglePlusStrategy());
        context.connect("Lokesh");
 
        System.out.println("====================");
 
        // Setting Orkut strategy.
        context.setSocialmediaStrategy(new OrkutStrategy());
        context.connect("Lokesh");
    }
}
Output:

Connecting with Lokesh through Facebook
====================
Connecting with Lokesh through Twitter
====================
Connecting with Lokesh through GooglePlus
====================
Connecting with Lokesh through Orkut [not possible though :)]

模板模式

模板方法設計模式是一種被普遍接受的行爲設計模式,用於在編程環境中實施某種算法(固定步驟集)。它定義了執行一個多步驟算法的順序步驟,還能夠提供一個默認實現.
模板法模式的適用性:
當咱們有預約義的步驟來實現一些算法時。
當咱們想要避免重複代碼時,移動基類中的公共實現和步驟。函數

假設咱們須要建造有特定步驟的房子。有些步驟有默認實現,有些步驟沒有默認實現。
統一施工的默認實現步驟:ui

  • 地基施工
  • 屋頂施工

個性化施工的實現的步驟(好比針對混凝土牆、玻璃牆、磚牆等)this

  • 牆體施工
  • 門窗施工
  • 繪畫
  • 室內裝飾

關鍵代碼:部分步驟在抽象類實現,其餘步驟在子類實現。spa

House.java
public abstract class House {
    /**
     * This is the template method we are discussing. This method should be
     * final so that other class can't re-implement and change the order of the
     * steps.
     */
    public final void buildhouse() {
        constructBase();
        constructRoof();
        constructWalls();
        constructWindows();
        constructDoors();
        paintHouse();
        decorateHouse();
    }
 
    public abstract void decorateHouse();
 
    public abstract void paintHouse();
 
    public abstract void constructDoors();
 
    public abstract void constructWindows();
 
    public abstract void constructWalls();
 
    /**
     * final implementation of constructing roof - final as all type of house
     * Should build roof in same manner.
     */
    private final void constructRoof() {
        System.out.println("Roof has been constructed.");
    }
 
    /**
     * final implementation of constructing base - final as all type of house
     * Should build base/foundation in same manner.
     */
    private final void constructBase() {
        System.out.println("Base has been constructed.");
    }
}
ConcreteWallHouse.java
public class ConcreteWallHouse extends House {
      @Override
      public void decorateHouse() {
            System.out.println(「Decorating Concrete Wall House」);
      }
      @Override
      public void paintHouse() {
            System.out.println(「Painting Concrete Wall House」);
      }
      @Override
      public void constructDoors() {
            System.out.println(「Constructing Doors for Concrete Wall House」);
      }
      @Override
      public void constructWindows() {
            System.out.println(「Constructing Windows for Concrete Wall House」);
      }
      @Override
      public void constructWalls() {
            System.out.println(「Constructing Concrete Wall for my House」);
      }
}
GlassWallHouse.java
public class GlassWallHouse extends House {
    @Override
    public void decorateHouse() {
        System.out.println("Decorating Glass Wall House");
    }
 
    @Override
    public void paintHouse() {
        System.out.println("Painting Glass Wall House");
    }
 
    @Override
    public void constructDoors() {
        System.out.println("Constructing Doors for Glass Wall House");
    }
 
    @Override
    public void constructWindows() {
        System.out.println("Constructing Windows for Glass Wall House");
    }
 
    @Override
    public void constructWalls() {
        System.out.println("Constructing Glass Wall for my House");
    }
}
Demo
public class Demo {
      public static void main(String[] args) {
 
            System.out.println(「Going to build Concrete Wall House」);
 
            House house = new ConcreteWallHouse();
            house.buildhouse();
 
            System.out.println(「Concrete Wall House constructed successfully」);
 
            System.out.println(「********************」);
 
            System.out.println(「Going to build Glass Wall House」);
 
            house = new GlassWallHouse();
            house.buildhouse();
 
            System.out.println(「Glass Wall House constructed successfully」);
      }
}
Output:

Going to build Concrete Wall House
Base has been constructed.
Roof has been constructed.
Constructing Concrete Wall for my House
Constructing Windows for Concrete Wall House
Constructing Doors for Concrete Wall House
Painting Concrete Wall House
Decorating Concrete Wall House
Concrete Wall House constructed successfully

********************

Going to build Glass Wall House
Base has been constructed.
Roof has been constructed.
Constructing Glass Wall for my House
Constructing Windows for Glass Wall House
Constructing Doors for Glass Wall House
Painting Glass Wall House
Decorating Glass Wall House
Glass Wall House constructed successfully

 

訪問者模式

訪問者設計模式是一種將算法與其操做的對象結構分離的方法。這種分離的一個實際結果是可以在不修改現有對象結構的狀況下向現有對象結構添加新操做。這是遵循開放/封閉原則(實體設計原則之一)的一種方法。
上述設計靈活性容許向任何對象層次結構添加方法,而無需修改成層次結構編寫的代碼。更確切地說,使用雙調度機制來實現此功能。Double dispatch是一種特殊的機制,它根據調用中涉及的兩個對象的運行時類型,將函數調用分派給不一樣的具體函數。

關鍵代碼:在數據基礎類裏面有一個方法接受訪問者,將自身引用傳入訪問者。

Router.java
public interface Router 
{
    public void sendData(char[] data);
    public void acceptData(char[] data);
     
    public void accept(RouterVisitor v);
}
DLinkRouter.java
public class DLinkRouter implements Router{
 
    @Override
    public void sendData(char[] data) {
    }
 
    @Override
    public void acceptData(char[] data) {
    }
 
    @Override
    public void accept(RouterVisitor v) {
        v.visit(this);
    }
}
LinkSysRouter.java
public class LinkSysRouter implements Router{
 
    @Override
    public void sendData(char[] data) {
    }
 
    @Override
    public void acceptData(char[] data) {
    }
     
    @Override
    public void accept(RouterVisitor v) {
        v.visit(this);
    }
}
TPLinkRouter.java
public class TPLinkRouter implements Router{
 
    @Override
    public void sendData(char[] data) {
    }
 
    @Override
    public void acceptData(char[] data) {
    }
     
    @Override
    public void accept(RouterVisitor v) {
        v.visit(this);
    }
}
RouterVisitor.java
public interface RouterVisitor {
    public void visit(DLinkRouter router);
    public void visit(TPLinkRouter router);
    public void visit(LinkSysRouter router);
}
LinuxConfigurator.java
public class LinuxConfigurator implements RouterVisitor{
 
    @Override
    public void visit(DLinkRouter router) {
        System.out.println("DLinkRouter Configuration for Linux complete !!");
    }
 
    @Override
    public void visit(TPLinkRouter router) {
        System.out.println("TPLinkRouter Configuration for Linux complete !!");
    }
 
    @Override
    public void visit(LinkSysRouter router) {
        System.out.println("LinkSysRouter Configuration for Linux complete !!");
    }
}
MacConfigurator.java
public class MacConfigurator implements RouterVisitor{
 
    @Override
    public void visit(DLinkRouter router) {
        System.out.println("DLinkRouter Configuration for Mac complete !!");
    }
 
    @Override
    public void visit(TPLinkRouter router) {
        System.out.println("TPLinkRouter Configuration for Mac complete !!");
    }
 
    @Override
    public void visit(LinkSysRouter router) {
        System.out.println("LinkSysRouter Configuration for Mac complete !!");
    }
}

TestVisitorPattern.java
public class TestVisitorPattern extends TestCase
{
    private MacConfigurator macConfigurator;
    private LinuxConfigurator linuxConfigurator;
    private DLinkRouter dlink;
    private TPLinkRouter tplink;
    private LinkSysRouter linksys;
     
    public void setUp()
    {
        macConfigurator = new MacConfigurator();
        linuxConfigurator = new LinuxConfigurator();
         
        dlink = new DLinkRouter();
        tplink = new TPLinkRouter();
        linksys = new LinkSysRouter();
    }
     
    public void testDlink()
    {
        dlink.accept(macConfigurator);
        dlink.accept(linuxConfigurator);
    }
     
    public void testTPLink()
    {
        tplink.accept(macConfigurator);
        tplink.accept(linuxConfigurator);
    }
     
    public void testLinkSys()
    {
        linksys.accept(macConfigurator);
        linksys.accept(linuxConfigurator);
    }
}
 
Output:
 
DLinkRouter Configuration for Mac complete !!
DLinkRouter Configuration for Linux complete !!
LinkSysRouter Configuration for Mac complete !!
LinkSysRouter Configuration for Linux complete !!
TPLinkRouter Configuration for Mac complete !!
TPLinkRouter Configuration for Linux complete !!
相關文章
相關標籤/搜索