導讀:設計模式能夠幫助我們優化代碼結構,讓代碼更優雅靈活。有哪些常見的設計模式?如何合理運用?本文分享作者對工廠模式、單例模式、裝飾模式、策略模式、代理模式和觀察者模式的理解,介紹每種模式的模式結構、優缺點、適用場景、註意實現及代碼實現。 ...
導讀:設計模式能夠幫助我們優化代碼結構,讓代碼更優雅靈活。有哪些常見的設計模式?如何合理運用?本文分享作者對工廠模式、單例模式、裝飾模式、策略模式、代理模式和觀察者模式的理解,介紹每種模式的模式結構、優缺點、適用場景、註意實現及代碼實現。
小明追妹子的時候,請她喝了不少咖啡,她愛喝卡布奇諾,每次去咖啡店,只要跟服務員說“來杯卡布奇諾”就行了,雖然各家的口味有些不同,但是不管是星爸爸還是Costa,都能夠提供卡布奇諾這種咖啡。這裡的星爸爸和Costa就是生產咖啡的工廠。
-
Factory:工廠角色-負責實現創建所有實例的內部邏輯.
-
Product:抽象產品角色-是所創建的所有對象的父類,負責描述所有實例所共有的公共介面。
-
ConcreteProduct:具體產品角色-是創建目標,所有創建的對象都充當這個角色的某個具體類的實例。
-
優點:客戶類和工廠類分開。消費者任何時候需要某種產品,只需向工廠請求即可。消費者無須修改就可以接納新產品。
-
缺點:是當產品修改時,工廠類也要做相應的修改。
以前經常帶老婆去優衣庫(簡單工廠)買衣服,就那麼多款式,逛的次數多了,她就煩了。後來我改變策略,帶老婆去逛商場(抽象工廠),商場里有各式品牌的店鋪,不用我管,她自己就能逛上一整天。 區別於簡單工廠,核心工廠類(商場)不再負責所有產品的創建,而是將具體創建的工作交給子類(服裝店)去做,成為一個抽象工廠角色,僅負責給出具體工廠類必須實現的介面(門店),而不接觸哪一個產品類應當被實例化這種細節。
-
Product:抽象產品
-
ConcreteProduct:具體產品
-
Factory:抽象工廠
-
ConcreteFactory:具體工廠
package FactoryMethod;
public class FactoryPattern
{
public static void main(String[] args)
{
Factory factory = new ConcreteFactoryA();
Product product = factory.createProduct();
product.use();
}
}
//抽象產品:提供了產品的介面
interface Product
{
public void use();
}
//具體產品A:實現抽象產品中的抽象方法
class ConcreteProductA implements Product
{
public void use()
{
System.out.println("具體產品A顯示...");
}
}
//具體產品B:實現抽象產品中的抽象方法
class ConcreteProductB implements Product
{
public void use()
{
System.out.println("具體產品B顯示...");
}
}
//抽象工廠:提供了廠品的生成方法
interface Factory
{
public Product createProduct();
}
//具體工廠A:實現了廠品的生成方法
class ConcreteFactoryA implements AbstractFactory
{
public Product createProduct()
{
System.out.println("具體工廠A生成-->具體產品A.");
return new ConcreteProductA();
}
}
//具體工廠B:實現了廠品的生成方法
class ConcreteFactoryB implements AbstractFactory
{
public Product createProduct()
{
System.out.println("具體工廠B生成-->具體產品B.");
return new ConcreteProductB();
}
}
韋小寶有7個老婆,但是每個都只有他這一個老公,他的所有老婆叫老公時,指的都是他,他就是一個單例。
-
Singleton:單例
-
優點:全局只有一個實例,便於統一控制,同時減少了系統資源開銷。
-
缺點:沒有抽象層,擴展困難。
public class Singleton
{
private static volatile Singleton instance=null; //保證 instance 在所有線程中同步
private Singleton(){} //private 避免類在外部被實例化
public static synchronized Singleton getInstance()
{
//getInstance 方法前加同步
if(instance == null)
{
instance = new Singleton();
}
return instance;
}
}
大學畢業,想要送給室友一個有紀念意義的禮物,就找到一張大家的合照,在上面寫上“永遠的兄弟!”,然後拿去禮品店裝了個相框,再包上禮盒。這裡的我和禮品店都是裝飾器,都沒有改變照片本身,卻都讓照片變得更適合作為禮物送人。
-
Component:抽象構件
-
ConcreteComponent:具體構件
-
Decorator:抽象裝飾類
-
ConcreteDecorator:具體裝飾類
-
優點:比繼承更加靈活(繼承是耦合度很大的靜態關係),可以動態的為對象增加職責,可以通過使用不同的裝飾器組合為對象擴展N個新功能,而不會影響到對象本身。
-
缺點:當一個對象的裝飾器過多時,會產生很多的裝飾類小對象和裝飾組合策略,增加系統複雜度,增加代碼的閱讀理解成本。
-
適合需要(通過配置,如:diamond)來動態增減對象功能的場景。
-
適合一個對象需要N種功能排列組合的場景(如果用繼承,會使子類數量爆炸式增長)
-
一個裝飾類的介面必須與被裝飾類的介面保持相同,對於客戶端來說無論是裝飾之前的對象還是裝飾之後的對象都可以一致對待。
-
儘量保持具體構件類Component作為一個“輕”類,也就是說不要把太多的邏輯和狀態放在具體構件類中,可以通過裝飾類。
package decorator;
public class DecoratorPattern
{
public static void main(String[] args)
{
Component component = new ConcreteComponent();
component.operation();
System.out.println("---------------------------------");
Component decorator = new ConcreteDecorator(component);
decorator.operation();
}
}
//抽象構件角色
interface Component
{
public void operation();
}
//具體構件角色
class ConcreteComponent implements Component
{
public ConcreteComponent()
{
System.out.println("創建具體構件角色");
}
public void operation()
{
System.out.println("調用具體構件角色的方法operation()");
}
}
//抽象裝飾角色
class Decorator implements Component
{
private Component component;
public Decorator(Component component)
{
this.component=component;
}
public void operation()
{
component.operation();
}
}
//具體裝飾角色
class ConcreteDecorator extends Decorator
{
public ConcreteDecorator(Component component)
{
super(component);
}
public void operation()
{
super.operation();
addBehavior();
}
public void addBehavior()
{
System.out.println("為具體構件角色增加額外的功能addBehavior()");
}
}
男生追妹子時,一般都會用到這種模式,常見的策略有這些:約會吃飯;看電影;看演唱會;逛街;去旅行……,雖然做的事情不同,但可以相互替換,唯一的目標都是捕獲妹子的芳心。
-
Context: 環境類
-
Strategy: 抽象策略類
-
ConcreteStrategy: 具體策略類
-
優點:策略模式提供了對“開閉原則”的完美支持,用戶可以在不修改原有系統的基礎上選擇演算法或行為。幹掉複雜難看的if-else。
-
缺點:調用時,必須提前知道都有哪些策略模式類,才能自行決定當前場景該使用何種策略。
package strategy;
public class StrategyPattern
{
public static void main(String[] args)
{
Context context = new Context();
Strategy strategyA = new ConcreteStrategyA();
context.setStrategy(strategyA);
context.algorithm();
System.out.println("-----------------");
Strategy strategyB = new ConcreteStrategyB();
context.setStrategy(strategyB);
context.algorithm();
}
}
//抽象策略類
interface Strategy
{
public void algorithm(); //策略方法
}
//具體策略類A
class ConcreteStrategyA implements Strategy
{
public void algorithm()
{
System.out.println("具體策略A的策略方法被訪問!");
}
}
//具體策略類B
class ConcreteStrategyB implements Strategy
{
public void algorithm()
{
System.out.println("具體策略B的策略方法被訪問!");
}
}
//環境類
class Context
{
private Strategy strategy;
public Strategy getStrategy()
{
return strategy;
}
public void setStrategy(Strategy strategy)
{
this.strategy=strategy;
}
public void algorithm()
{
strategy.algorithm();
}
}
淘寶店客服總是會收到非常多的重覆問題,例如:有沒有現貨?什麼時候發貨?發什麼快遞?大量回答重覆性的問題太煩了,於是就出現了小蜜機器人,他來幫客服回答那些已知的問題,當碰到小蜜無法解答的問題時,才會轉到人工客服。這裡的小蜜機器人就是客服的代理。
-
Subject: 抽象主題角色
-
Proxy: 代理主題角色
-
RealSubject: 真實主題角色
-
優點:代理可以協調調用方與被調用方,降低了系統的耦合度。根據代理類型和場景的不同,可以起到控制安全性、減小系統開銷等作用。
-
缺點:增加了一層代理處理,增加了系統的複雜度,同時可能會降低系統的相應速度。
-
遠程(Remote)代理:為一個位於不同的地址空間的對象提供一個本地的代理對象,這個不同的地址空間可以是在同一臺主機中,也可是在另一臺主機中,遠程代理又叫做大使(Ambassador)。
-
虛擬(Virtual)代理:如果需要創建一個資源消耗較大的對象,先創建一個消耗相對較小的對象來表示,真實對象只在需要時才會被真正創建。
-
Copy-on-Write代理:它是虛擬代理的一種,把複製(克隆)操作延遲到只有在客戶端真正需要時才執行。一般來說,對象的深克隆是一個開銷較大的操作,Copy-on-Write代理可以讓這個操作延遲,只有對象被用到的時候才被克隆。
-
保護(Protect or Access)代理:控制對一個對象的訪問,可以給不同的用戶提供不同級別的使用許可權。
-
緩衝(Cache)代理:為某一個目標操作的結果提供臨時的存儲空間,以便多個客戶端可以共用這些結果。
-
防火牆(Firewall)代理:保護目標不讓惡意用戶接近。
-
同步化(Synchronization)代理:使幾個用戶能夠同時使用一個對象而沒有衝突。
-
智能引用(Smart Reference)代理:當一個對象被引用時,提供一些額外的操作,如將此對象被調用的次數記錄下來等。
package proxy;
public class ProxyPattern
{
public static void main(String[] args)
{
Proxy proxy = new Proxy();
proxy.request();
}
}
//抽象主題
interface Subject
{
void request();
}
//真實主題
class RealSubject implements Subject
{
public void request()
{
System.out.println("訪問真實主題方法...");
}
}
//代理
class Proxy implements Subject
{
private RealSubject realSubject;
public void request()
{
if (realSubject==null)
{
realSubject=new RealSubject();
}
preRequest();
realSubject.request();
afterRequest();
}
public void preRequest()
{
System.out.println("訪問真實主題之前的預處理。");
}
public void afterRequest()
{
System.out.println("訪問真實主題之後的後續處理。");
}
}
出差在外,想瞭解孩子在家的情況,這時候只要加入“相親相愛一家人”群,老爸老媽會經常把孩子的照片和視頻發到群里,你要做的就是作為一個觀察者,刷一刷群里的信息就能夠瞭解一切了。
-
Subject:目標
-
ConcreteSubject:具體目標
-
Observer:觀察者
-
ConcreteObserver:具體觀察者
-
優點:將複雜的串列處理邏輯變為單元化的獨立處理邏輯,被觀察者只是按照自己的邏輯發出消息,不用關心誰來消費消息,每個觀察者只處理自己關心的內容。邏輯相互隔離帶來簡單清爽的代碼結構。
-
缺點:觀察者較多時,可能會花費一定的開銷來發消息,但這個消息可能僅一個觀察者消費。
package observer;
import java.util.*;
public class ObserverPattern
{
public static void main(String[] args)
{
Subject subject = new ConcreteSubject();
Observer obsA = new ConcreteObserverA();
Observer obsb = new ConcreteObserverB();
subject.add(obsA);
subject.add(obsB);
subject.setState(0);
}
}
//抽象目標
abstract class Subject
{
protected List<Observer> observerList = new ArrayList<Observer>();
//增加觀察者方法
public void add(Observer observer)
{
observers.add(observer);
}
//刪除觀察者方法
public void remove(Observer observer)
{
observers.remove(observer);
}
public abstract void notify(); //通知觀察者方法
}
//具體目標
class ConcreteSubject extends Subject
{
private Integer state;
public void setState(Integer state){
this.state = state;
// 狀態改變通知觀察者
notify();
}
public void notify()
{
System.out.println("具體目標狀態發生改變...");
System.out.println("--------------");
for(Observer obs:observers)
{
obs.process();
}
}
}
//抽象觀察者
interface Observer
{
void process(); //具體的處理
}
//具體觀察者A
class ConcreteObserverA implements Observer
{
public void process()
{
System.out.println("具體觀察者A處理!");
}
}
//具體觀察者B
class ConcreteObserverB implements Observer
{
public void process()
{
System.out.println("具體觀察者B處理!");
}
}
作者:
本文來自博客園,作者:古道輕風,轉載請註明原文鏈接:https://www.cnblogs.com/88223100/p/How-to-understand-these-six-common-design-patterns.html