最近在讀秦小波的《設計模式之禪》。本文又是一篇長達2000行的又水又長的筆記,記錄書中所講23個設計模式中的22個,基本上是將書中講的各個設計模式的定義、優點、缺點、適用場景、demo抄下來了。推薦去閱讀原書,這是一位學識豐富且有一個有趣的靈魂的作者所寫,原書中每個設計模式的講解都有一個十分有趣的例 ...
最近在讀秦小波的《設計模式之禪》。本文又是一篇長達2000行的又水又長的筆記,記錄書中所講23個設計模式中的22個,基本上是將書中講的各個設計模式的定義、優點、缺點、適用場景、demo抄下來了。推薦去閱讀原書,這是一位學識豐富且有一個有趣的靈魂的作者所寫,原書中每個設計模式的講解都有一個十分有趣的例子,藉助這些例子可以很好的加深理解和記憶。
設計模式感覺都是相通的,之前看過一本《JavaScript設計模式與開發實踐》,其實都是講的一回事,只是因為語言特性原因在實現方面有所區別,這本書的作者也有一個有趣的靈魂,我現在都還記得代理模式中“小明追美眉”的故事,也推薦閱讀。
01單例模式
定義:
確保某一個類只有一個實例,而且自行實例化並向整個系統提供這個實例
示例:
public class Singleton {
private static Singleton singleton = new Singleton();
// 私有化構造
private Singleton() {
}
// 獲取實例
public static Singleton getInstance() {
return singleton;
}
// 類中其他方法儘量是static的
public static void otherMethod() {
}
}
優點:
- 單例模式只生成一個實例,所以減少了系統的性能開銷
- 單例模式可以避免對資源的多重占用
- 在記憶體中,避免對同一資源占用
缺點:
- 一般沒有介面,擴展困難
- 對測試不利
- 與單一職責原則違背
適用場景:
- 要求生成唯一序列號環境
- 創建一個對象需要消耗的資源過多
- 需要定義大量的靜態常量和靜態方法(如工具類)的環境
02工廠方法模式
定義:
定義一個用於創建對象的介面,讓子類決定實例化哪一個類。工廠方法使一個類的實例化延遲到其子類
示例:
/**
* 抽象產品
*/
public abstract class Product {
public void method1() {
System.out.println("method1");
}
abstract public void method2();
}
/**
* 具體的一個產品
*/
public class ProductA extends Product {
public void method2() {
System.out.println("productA method2");
}
}
/**
* 具體的一個產品
*/
public class ProductB extends Product {
public void method2() {
System.out.println("productB method2");
}
}
/**
* 抽象工廠
*/
public abstract class Factory {
public abstract <T extends Product> T createProduct(Class<T> clazz);
}
/**
* 具體工廠
*/
public class ProductFactory1 extends Factory {
public <T extends Product> T createProduct(Class<T> clazz) {
Product product = null;
try {
product = (Product) Class.forName(clazz.getName()).newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return (T) product;
}
}
/**
* 測試類
*/
public class FactoryTest {
@Test
public void test() {
Factory factory = new ProductFactory1();
ProductA productA = factory.createProduct(ProductA.class);
ProductB productB = factory.createProduct(ProductB.class);
productA.method2();
productB.method2();
}
}
優點:
- 良好的封裝性,代碼結構清晰
- 工廠方法模式的擴展性非常優秀,可以很方便的增加產品類
- 屏蔽產品類。只需要關心產品的介面,無需關心實現
- 工廠方法模式是典型的解耦框架
使用場景:
- 工廠方法模式是new一個對象的替代品,但是需要考慮是否需要使用工廠增加代碼複雜度
- 需要靈活的、可擴展的框架時,可以考慮採用工廠方法模式
- 工廠方法模式可以用在異構項目中
- 可以使用在測試驅動開發的框架下
工廠模式擴展:
- 使用靜態工廠
/**
* 靜態工廠
*/
public class ProductFactory2 {
private ProductFactory2() {
}
static Product createProduct(Class<Product> clazz) {
Product product = null;
try {
product = (Product) Class.forName(clazz.getName()).newInstance();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
}
return product;
}
}
-
多工廠模式
-
實現單例模式
-
延遲初始化
03抽象工廠模式
定義:
為創建一組相關或相互依賴的對象提供一個介面,而且無須指定它們的具體類
示例:
public class Factory {
public ProductA createProductA() {
return new ProductA();
}
public ProductB createProductB() {
return new ProductB();
}
}
優點:
- 封裝性, 只要知道工廠類是誰就能創建出對象
- 產品族內的約束為非公開狀態。具體是用哪個產品類創建的是非公開的
缺點:
- 難以加產品,沒加一個產品都需要改工廠
適用場景:
一個對象族(或是一組沒有任何關係的對象)都有相同的約束,則可以使用抽象工廠模式。就是一堆類有相同的父類
04模板方法模式
定義:
定義一個操作中的演算法的框架,而將一些步驟延遲到子類中。使得子類可以不改變一個演算法的結構即可重定義該演算法的某些特定步驟
示例:
/**
* 模板類
*/
public abstract class TemplateClass {
protected abstract void method1();
protected abstract void method2();
/**
* 模板方法
*/
public void templateMethod() {
method1();
method2();
}
}
/**
* 根據模板類創建的類
*/
public class TemplateSubClass1 extends TemplateClass {
protected void method1() {
System.out.println("1 method1");
}
protected void method2() {
System.out.println("1 method2");
}
}
/**
* 根據模板類創建的類
*/
public class TemplateSubClass2 extends TemplateClass {
protected void method1() {
System.out.println("2 method1");
}
protected void method2() {
System.out.println("2 method2");
}
}
/**
* 測試類
*/
public class TemplateTest {
@Test
public void test() {
TemplateSubClass1 templateSubClass1 = new TemplateSubClass1();
templateSubClass1.templateMethod();
TemplateSubClass2 templateSubClass2 = new TemplateSubClass2();
templateSubClass2.templateMethod();
}
}
優點:
- 封裝不變部分,擴展可變部分
- 提取公共部分代碼,便於維護
- 行為由父類控制,子類實現
缺點:
子類對父類產生了影響,在複雜的項目中,會帶來代碼閱讀的難度,會讓新手難以適應
適用場景:
多個子類有公有的方法,並且邏輯基本相同
模板方法模式擴展:
可以通過定義開關的形式,調整模板方法的執行流程
05建造者模式
定義:
將一個複雜對象的構建與它的表示分離,使得同樣的構建過程可以創建不同的表示
示例:
public class Product {
private String name;
private int price;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
}
public class ProductBuilder {
private Product product = new Product();
public void buildName(String name) {
product.setName(name);
}
public void buildPrice(Integer price) {
product.setPrice(price);
}
public Product create() {
return product;
}
}
優點:
- 使用建造者模式可以使客戶端不必知道產品內部組成的細節
- 建造者獨立,容易擴展
- 由於具體的建造者是獨立的,因此可以對建造過程逐步細化,而不對其他的模塊產生任何影響
缺點:
- 如果產品之間的差異性很大,則不適合使用建造者模式,因此其使用範圍受到一定的限制
- 如果產品的內部變化複雜,可能會導致需要定義很多具體建造者類來實現這種變化,導致系統變得很龐大
適用場景:
- 相同的方法,不同的執行順序,產生不同的事件結果時,可以採用建造者模式
- 產品類非常複雜,或者產品類中的調用順序不同產生了不同的效能
- 在對象創建過程中會使用到系統中的一些其他對象,這些對象在產品對象的創建過程中不易得到時
06代理模式
定義:
為其他對象提供一種代理以控制對這個對象的訪問
示例:
/**
* 被代理類介面
*/
public interface Subject {
void doSomething();
}
/**
* 真實被代理類
*/
public class RealSubject implements Subject {
public void doSomething() {
System.out.println("RealSubject doSomething");
}
}
/**
* 代理類
*/
public class SubjectProxy implements Subject {
private Subject subject = null;
public SubjectProxy() {
this.subject = new RealSubject();
}
public void doSomething() {
System.out.println("doSomething 被代理了");
this.subject.doSomething();
}
}
/**
* 測試類
*/
public class SubjectProxyTest {
@Test
public void test() {
SubjectProxy subjectProxy = new SubjectProxy();
subjectProxy.doSomething();
}
}
優點:
- 職責清晰。真實類關註自己的邏輯,臟活累活交給代理類
- 高可擴展性。對真實類的變更,不需要動代理類
適用場景:
- 需要無侵入的對真實類做一些擴展,增強
- 期望控制對真實類的訪問
代理模式擴展:
- 普通代理
就是示例那個,調用方知道代理的存在(類似正向代理)
- 透明代理
調用方不知道代理的存在(類似反向代理)
/**
* 真實被代理類
*/
public class RealSubject implements Subject {
public RealSubject(Subject subject) throws ClassNotFoundException {
// 註意這裡
if (subject == null) {
throw new ClassNotFoundException();
}
}
public void doSomething() {
System.out.println("RealSubject doSomething");
}
}
/**
* 代理類
*/
public class SubjectProxy implements Subject {
private Subject subject = null;
public SubjectProxy() {
// 註意這裡
try {
this.subject = new RealSubject(this);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
public void doSomething() {
System.out.println("doSomething 被代理了");
this.subject.doSomething();
}
}
/***
* 測試類
*/
public class ProxyTest {
@Test
public void test() {
SubjectProxy subjectProxy = new SubjectProxy();
subjectProxy.doSomething();
}
@Test
public void test2() throws ClassNotFoundException {
// 會報錯的
// RealSubject realSubject = new RealSubject();
// realSubject.doSomething();
}
}
- 強制代理
必須通過真實角色查找到代理角色,否則你不能訪問
感覺意義不是太大
- 動態代理
動態代理是在實現階段不用關心代理誰,而在運行階段才指定代理哪一個對象
/**
* 通知介面
*/
public interface Advice {
public void exec();
}
/**
* 通知類
*/
public class BeforeAdvice implements Advice {
public void exec() {
System.out.println("前置通知執行了");
}
}
/**
* 動態代理的handler
*/
public class MyInvocationHandler implements InvocationHandler {
// 被代理對象
private Object target;
public MyInvocationHandler(Object target) {
this.target = target;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 尋找JoinPoint連結點
if (true) {
new BeforeAdvice().exec();
}
// 執行被代理的方法
return method.invoke(this.target, args);
}
}
/**
* 動態代理類
*/
public class SubjectDynamicProxy {
public static Subject newInstance(Subject subject) {
ClassLoader classLoader = subject.getClass().getClassLoader();
Class<?>[] interfaces = subject.getClass().getInterfaces();
InvocationHandler invocationHandler = new MyInvocationHandler(subject);
return (Subject) Proxy.newProxyInstance(classLoader, interfaces, invocationHandler);
}
}
/**
* 測試類
*/
public class ProxyTest {
@Test
public void test() {
Subject subject = new RealSubject();
Subject proxySubject = SubjectDynamicProxy.newInstance(subject);
proxySubject.doSomething();
}
}
使用jdk的動態代理要求被代理類必須實現一個介面,CGLIB動態代理沒有這個要求。
spring 的aop就是使用的動態代理技術實現的
07原型模式
定義:
用原型實例指定創建對象的種類,並且通過拷貝這些原型創建新的對象。即基於對象創建對象,而不是類
示例:
public class Prototype implements Cloneable {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
protected Prototype clone() throws CloneNotSupportedException {
// 關鍵就是這個東西
return (Prototype) super.clone();
}
}
優點:
- 性能優良:基於二進位流拷貝,比new性能高
- 逃避構造函數的約束
適用場景:
- 資源優化場景。類的初始化需要消耗很多資源
- 一個對象多個修改者的場景
註意點:
- 深拷貝與淺拷貝
- clone與final不能並存
- 拷貝時構造方法不會執行
08中介者模式
定義:
用一個中介對象封裝一系列的對象交互,中介者使各對象不需要顯示地相互作用,從而使其耦合鬆散,而且可以獨立地改變它們之間的交互
示例:
/**
* 同事抽象類
*/
public abstract class Colleagure {
protected Mediator mediator;
public Colleagure(Mediator mediator) {
this.mediator = mediator;
}
}
/**
* 具體同事類
*/
public class ConcreteColleagure1 extends Colleagure {
public ConcreteColleagure1(Mediator mediator) {
super(mediator);
}
public void selfMethod1() {
System.out.println("ConcreteColleagure1 自己的業務邏輯");
}
public void depMethod1() {
// 依賴其他對象的邏輯,給中介者處理
super.mediator.doSomething1();
}
}
/**
* 具體同事類
*/
public class ConcreteColleagure2 extends Colleagure {
public ConcreteColleagure2(Mediator mediator) {
super(mediator);
}
public void selfMethod1() {
System.out.println("ConcreteColleagure2 自己的業務邏輯");
}
public void depMethod1() {
// 依賴其他對象的邏輯,給中介者處理
super.mediator.doSomething2();
}
}
/**
* 抽象中介者
*/
public abstract class Mediator {
protected ConcreteColleagure1 concreteColleagure1;
protected ConcreteColleagure2 concreteColleagure2;
public abstract void doSomething1();
public abstract void doSomething2();
public ConcreteColleagure1 getConcreteColleagure1() {
return concreteColleagure1;
}
public void setConcreteColleagure1(ConcreteColleagure1 concreteColleagure1) {
this.concreteColleagure1 = concreteColleagure1;
}
public ConcreteColleagure2 getConcreteColleagure2() {
return concreteColleagure2;
}
public void setConcreteColleagure2(ConcreteColleagure2 concreteColleagure2) {
this.concreteColleagure2 = concreteColleagure2;
}
}
/**
* 具體中介者
*/
public class ConcreteMediator extends Mediator {
public void doSomething1() {
super.concreteColleagure1.selfMethod1();
super.concreteColleagure2.selfMethod1();
}
public void doSomething2() {
super.concreteColleagure2.selfMethod1();
super.concreteColleagure1.selfMethod1();
}
}
/**
* 測試類
*/
public class MediatorTest {
@Test
public void test() {
Mediator mediator = new ConcreteMediator();
ConcreteColleagure2 concreteColleagure2 = new ConcreteColleagure2(mediator);
ConcreteColleagure1 concreteColleagure1 = new ConcreteColleagure1(mediator);
mediator.setConcreteColleagure1(concreteColleagure1);
mediator.setConcreteColleagure2(concreteColleagure2);
concreteColleagure1.depMethod1();
concreteColleagure2.depMethod1();
}
}
優點:
中介者模式的優點就是減少類間的依賴,把原有的一對多的依賴變成了一對一的依賴,實現瞭解耦
適用場景:
- 機場調度中心
- MVC框架
- 媒體網關
- 中介服務
09命令模式
定義:
將一個請求封裝成一個對象,從而讓你使用不同的請求把客戶端參數化,對請求排隊或者記錄請求日誌,可以提供命令的撤銷和恢復功能
示例:
/**
* 抽象接收者
*/
public abstract class Recevier {
public abstract void doSomething();
}
/**
* 具體的接收者
*/
public class ConcreteRecevier1 extends Recevier {
public void doSomething() {
System.out.println("ConcreteRecevier1 業務邏輯");
}
}
/**
* 具體的接收者
*/
public class ConcreteRecevier2 extends Recevier {
public void doSomething() {
System.out.println("ConcreteRecevier2 業務邏輯");
}
}
/**
* 抽象命令類
*/
public abstract class Command {
public abstract void execute();
}
/**
* 具體命令類
*/
public class ConcreteCommand1 extends Command {
Recevier recevier;
public ConcreteCommand1(Recevier recevier) {
this.recevier = recevier;
}
public ConcreteCommand1() {
this(new ConcreteRecevier1());
}
public void execute() {
recevier.doSomething();
}
}
/**
* 具體命令類
*/
public class ConcreteCommand2 extends Command {
Recevier recevier;
public ConcreteCommand2(Recevier recevier) {
this.recevier = recevier;
}
public ConcreteCommand2() {
this(new ConcreteRecevier2());
}
public void execute() {
recevier.doSomething();
}
}
/**
* 調用者
*/
public class Invoker {
private Command command;
public void setCommand(Command command) {
this.command = command;
}
public void action() {
this.command.execute();
}
}
/**
* 測試類
*/
public class CommandTest {
@Test
public void test() {
Command command = new ConcreteCommand1();
Command command1 = new ConcreteCommand2();
Invoker invoker = new Invoker();
invoker.setCommand(command);
invoker.action();
invoker.setCommand(command1);
invoker.action();
}
}
優點:
- 解耦:調用者和接受者之間沒有依賴關係
- 命令很容易擴展
缺點:
- 命令的增加會帶來類數量的增加
10責任鏈模式
定義:
使多個對象都有機會處理請求,從而避免了請求的發送者和接受者之間的耦合關係。將這些對象連成一條鏈,並沿著這條鏈傳遞該請求,直到有對象處理它為止
示例:
/**
* 處理級別
*/
public enum Level {
LEVEL1(1, "級別1"),
LEVEL2(2, "級別2"),
LEVEL3(3, "級別3");
private int code;
private String msg;
private Level(int code, String msg) {
this.code = code;
this.msg = msg;
}
public int getCode() {
return code;
}
public String getMsg() {
return msg;
}
}
/**
* 請求封裝
*/
public class Request {
Level level;
public Request(Level level) {
this.level = level;
}
public Level getRequestLevel() {
return this.level;
}
}
/**
* 響應封裝
*/
public class Response {
private int code;
private String msg;
public Response(int code, String msg) {
this.code = code;
this.msg = msg;
}
public int getCode() {
return code;
}
public String getMsg() {
return msg;
}
}
/**
* 處理請求抽象
*/
public abstract class Handler {
private Handler nextHandler;
public void setNextHandler(Handler nextHandler) {
this.nextHandler = nextHandler;
}
public final Response handleMessage(Request request) {
Response response = null;
if (this.getHandleLevel().getCode() == request.getRequestLevel().getCode()) {
// 自己處理
response = this.doRequest(request);
} else {
if (this.nextHandler != null) {
// 交給下一個人處理
response = this.nextHandler.handleMessage(request);
} else {
response = new Response(-1, "無人處理");
}
}
return response;
}
// 獲取當前處理者能夠處理的級別
protected abstract Level getHandleLevel();
// 處理請求
protected abstract Response doRequest(Request request);
}
/**
* 具體處理請求的類
*/
public class Level1Handler extends Handler {
protected Level getHandleLevel() {
return Level.LEVEL1;
}
protected Response doRequest(Request request) {
return new Response(1, "第一個級別在Level1Handler處理了");
}
}
public class Level2Handler extends Handler {
protected Level getHandleLevel() {
return Level.LEVEL2;
}
protected Response doRequest(Request request) {
return new Response(1, "第2個級別在Level2Handler處理了");
}
}
public class Level3Handler extends Handler {
protected Level getHandleLevel() {
return Level.LEVEL3;
}
protected Response doRequest(Request request) {
return new Response(1, "第2個級別在Level3Handler處理了");
}
}
/**
* 測試類
*/
public class RequestChainTest {
@Test
public void test() {
Level1Handler level1Handler = new Level1Handler();
Level2Handler level2Handler = new Level2Handler();
Level3Handler level3Handler = new Level3Handler();
level1Handler.setNextHandler(level2Handler);
level2Handler.setNextHandler(level3Handler);
Response response = level1Handler.handleMessage(new Request(Level.LEVEL2));
System.out.println(response.getCode());
System.out.println(response.getMsg());
}
}
優點:
- 將請求和處理分開:請求者可以不用知道是誰處理的,處理者可以不用知道請求的全貌
11裝飾者模式
定義:
動態地給一個對象添加一些額外的職責。就增加功能來說,裝飾模式相比生成子類更為靈活
示例:
/**
* 被裝飾類的抽象
*/
public abstract class Component {
public abstract void doSomething();
}
/**
* 具體被裝飾者
*/
public class ConcreteComponent extends Component {
public void doSomething() {
System.out.println("被裝飾類的方法");
}
}
/**
* 裝飾類
*/
public class Decorator extends Component {
private Component component;
public Decorator(Component component) {
this.component = component;
}
@Override
public void doSomething() {
this.component.doSomething();
}
public void newMethod() {
System.out.println("新增一個方法");
}
}
優點:
- 裝飾類和被裝飾類可以獨立發展,而不會相互耦合
- 裝飾模式是繼承關係的一個替代方案
- 裝飾模式可以動態地擴展一個實現類的功能
缺點:
多層的裝飾是比較複雜的
適用場景:
- 需要擴展一個類
12策略模式
定義:
定義一組演算法,將每個演算法都封裝起來,並且使它們之間可以互換
示例:
/**
* 策略介面
*/
public interface Strategy {
void doSomething();
}
/**
* 具體的策略
*/
public class Strategy1 implements Strategy {
public void doSomething() {
System.out.println("第一種策略");
}
}
/**
* 具體的策略
*/
public class Strategy2 implements Strategy {
public void doSomething() {
System.out.println("第一種策略");
}
}
/**
* 封裝策略
*/
public class Context {
private Strategy strategy;
public void setStrategy(Strategy strategy) {
this.strategy = strategy;
}
public void execute() {
this.strategy.doSomething();
}
}
/**
* 測試類
*/
public class StrategyTest {
@Test
public void test() {
Context context = new Context();
context.setStrategy(new Strategy1());
context.execute();
}
}
優點:
- 演算法可以自由切換
- 避免使用多重條件判斷
- 擴展性良好,可以很方便的增加一個策略
缺點:
- 策略類數量增多
- 所有的策略類都需要對外暴露
適用場景:
- 演算法可以自由切換場景
- 需要屏蔽演算法規則的場景
13適配器模式
定義:
將一個類的介面變換成客戶端所期待的另一種介面,從而使原本因介面不匹配而無法在一起工作的兩個類能夠在一起工作
示例:
/**
* 目標角色
*/
public interface Target {
void request();
}
/**
* 源角色
*/
public class Adaptee {
public void doAnyThing() {
System.out.println("doAnyThing");
}
}
/**
* 適配器角色
*/
public class Atapter extends Adaptee implements Target {
public void request() {
super.doAnyThing();
}
}
優點:
- 適配器模式可以讓兩個沒有任何關係的類在一起運行
- 加了類的透明性
- 提高了類的復用度
- 靈活性非常好
適用場景:
當要改一個已經在生產中適用的類時可以考慮
14迭代器模式
定義:
它提供一種方法訪問一個容器對象中各個元素,而又不需暴露該對象的內部細節
示例:
很多集合類都實現了Iterable介面,就是迭代器模式
優點:
遍歷方便,隱藏內部細節
15組合模式
定義:
將對象組合成樹形結構以表示“部分-整體”的層次結構,使得用戶對單個對象和組合對象的使用具有一致性
示例:
/**
* 抽象構件
*/
public abstract class Component {
public void doSomething() {
System.out.println("一些業務邏輯");
}
}
/**
* 樹枝
*/
public class Composite extends Component {
/**
* 子節點容器
*/
private ArrayList<Component> componentArrayList = new ArrayList<Component>();
// 加節點
public void add(Component component) {
this.componentArrayList.add(component);
}
// 刪節點
public void remove(Component component) {
this.componentArrayList.remove(component);
}
// 獲取子節點
public ArrayList<Component> getChildren() {
return this.componentArrayList;
}
}
public class Leaf extends Component {
@Override
public void doSomething() {
System.out.println("葉子");
super.doSomething();
}
}
/**
* 測試類
*/
public class CompositeTest {
@Test
public void test() {
Composite root = new Composite();
root.doSomething();
Composite branch = new Composite();
Leaf leaf = new Leaf();
root.add(branch);
branch.add(leaf);
display(root);
}
private void display(Composite root) {
for (Component component1 : root.getChildren()) {
if (component1 instanceof Leaf) {
component1.doSomething();
} else {
display((Composite) component1);
}
}
}
}
優點:
- 高層模塊調用簡單
- 節點自由增加
適用場景:
-
維護和展示部分-整體關係的場景
-
從一個整體中能夠獨立出部分模塊或功能的場景
16觀察者模式
定義:
定義對象間一種一對多的依賴關係,使得每當一個對象改變狀態,則所有依賴於它的對象都會得到通知並被自動更新
示例:
/**
* 觀察者介面
*/
public interface Observer {
// 更新
void update(Object object);
}
/**
* 具體的觀察者
*/
public class ConcreteObserver implements Observer {
public void update(Object object) {
System.out.println("收到了通知"+object.toString());
}
}
/**
* 被觀察者抽象類
*/
public abstract class Subject {
private ArrayList<Observer> observers = new ArrayList<Observer>();
public void addObserver(Observer observer) {
this.observers.add(observer);
}
public void removeObserver(Observer observer) {
this.observers.remove(observer);
}
public void notifyObservers(Object object) {
for (Observer observer : this.observers) {
observer.update(object);
}
}
}
/**
* 具體的被觀察者
*/
public class ConcreteSubject extends Subject {
public void doSomething() {
super.notifyObservers("doSomething");
}
}
/**
* 測試類
*/
public class ObserverTest {
@Test
public void test() throws InterruptedException {
Observer observer = new ConcreteObserver();
ConcreteSubject subject = new ConcreteSubject();
subject.addObserver(observer);
Thread.sleep(1000);
subject.doSomething();
}
}
優點:
- 觀察者和被觀察者之間是抽象耦合
- 建立一套觸發機制
缺點:
- 開發效率問題
- 運行效率問題
適用場景:
- 跨系統的消息交換場景
- 關聯行為場景
17門面模式
定義:
要求一個子系統的外部與其內部的通信必須通過一個統一的對象進行。門面模式提供一個高層次的介面,使得子系統更易於使用
示例:
/**
* 子系統
*/
public class SubSystemA {
public void doSomethingA() {
System.out.println("doSomethingA");
}
}
/**
* 子系統
*/
public class SubSystemB {
public void doSomethingB() {
System.out.println("doSomethingB");
}
}
/**
* 子系統
*/
public class SubSystemC {
public void doSomethingC() {
System.out.println("doSomethingC");
}
}
/**
* 門面
*/
public class Facade {
private SubSystemA subSystemA = new SubSystemA();
private SubSystemB subSystemB = new SubSystemB();
private SubSystemC subSystemC = new SubSystemC();
public void methodA() {
subSystemA.doSomethingA();
}
public void methodB() {
subSystemB.doSomethingB();
}
public void methodC() {
subSystemC.doSomethingC();
}
}
優點:
- 減少系統的相互依賴
- 提高了靈活性
- 提高了安全性
缺點:
- 不符合開閉原則
適用場景:
- 為一個複雜的模塊或子系統提供一個供外界訪問的介面
- 子系統相對獨立
- 預防低水平人員帶來的風險擴散
18備忘錄模式
定義:
在不破壞封裝性的前提下,捕獲一個對象的內部狀態,併在該對象之外保存這個狀態。這樣以後就可將該對象恢復到原先保存的狀態
示例:
/**
* 發起人角色
*/
public class Originator {
private String state; // 內部狀態
public Originator() {
}
public Originator(String state) {
this.state = state;
}
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
// 創建備忘錄
public Memento createMemento() {
return new Memento(this.state);
}
// 恢復備忘錄
public void restoreMemento(Memento memento) {
this.setState(memento.getState());
}
public void changeState(String state) {
this.setState(state);
}
}
/**
* 備忘錄
*/
public class Memento {
private String state; // 發起人內部狀態
public Memento(String state) {
this.state = state;
}
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
}
/**
* 備忘錄管理員角色
*/
public class Caretaker {
private Memento memento;
public Memento getMemento() {
return memento;
}
public void setMemento(Memento memento) {
this.memento = memento;
}
}
/**
* 測試類
*/
public class MementoTest {
@Test
public void test() {
Originator originator = new Originator("1");
System.out.println(originator.getState());
Caretaker caretaker = new Caretaker();
caretaker.setMemento(originator.createMemento());
originator.changeState("3");
System.out.println(originator.getState());
originator.restoreMemento(caretaker.getMemento());
System.out.println(originator.getState());
}
}
適用場景:
- 需要保存和恢複數據的相關狀態場景
19訪問者模式
定義:
封裝一些作用於某種數據結構中的各元素的操作,它可以在不改變數據結構的前提下定義作用於這些元素的新的操作
示例:
/**
* 訪問者介面
*/
public interface IVisitor {
void visit(ConcreteElement1 concreteElement1);
void visit(ConcreteElement2 concreteElement2);
}
/**
* 具體的訪問者
*/
public class Visitor implements IVisitor {
public void visit(ConcreteElement1 concreteElement1) {
concreteElement1.doSomething();
}
public void visit(ConcreteElement2 concreteElement2) {
concreteElement2.doSomething();
}
}
/**
* 抽象元素
*/
public abstract class Element {
public abstract void doSomething(); // 業務邏輯
public abstract void accept(IVisitor visitor); // 允許誰訪問
}
/**
* 具體元素
*/
public class ConcreteElement1 extends Element {
public void doSomething() {
System.out.println("element1 業務邏輯");
}
public void accept(IVisitor visitor) {
visitor.visit(this);
}
}
/**
* 具體元素
*/
public class ConcreteElement2 extends Element {
public void doSomething() {
System.out.println("element2 業務邏輯");
}
public void accept(IVisitor visitor) {
visitor.visit(this);
}
}
/**
* 結構對象
*/
public class ObjectStructure {
public static Element createElement() {
Random random = new Random();
if(random.nextInt(100) < 50) {
return new ConcreteElement1();
} else {
return new ConcreteElement2();
}
}
}
/**
* 測試類
*/
public class VisitorTest {
@Test
public void test() {
for (int i = 0; i < 10; i++) {
Element e = ObjectStructure.createElement();
e.accept(new Visitor());
}
}
}
優點:
- 符合單一職責原則
- 可擴展性高
- 靈活性高
缺點:
- 具體元素變更比較困難
- 具體元素對訪問者公佈細節
- 違背了依賴倒置轉原則
適用場景:
業務規則要求遍歷多個不同的對象
20狀態模式
定義:
當一個對象內在狀態改變時允許其改變行為,這個對象看起來像改變了其類
示例:
/**
* 狀態抽象類
*/
public abstract class State {
protected Context context;
public void setContext(Context context) {
this.context = context;
}
// 行為1
public abstract void handle1();
// 行為2
public abstract void hanle2();
}
/**
* 具體狀態
*/
public class State1 extends State {
public void handle1() {
}
public void hanle2() {
super.context.setCurrentState(Context.STATE2);
System.out.println("在狀態1可以執行 handle2");
}
}
/**
* 具體狀態
*/
public class State2 extends State {
public void handle1() {
super.context.setCurrentState(Context.STATE1);
System.out.println("在狀態2可以執行 handle1");
}
public void hanle2() {
}
}
/**
* 環境角色
*/
public class Context {
public static final State STATE1 = new State1();
public static final State STATE2 = new State2();
private State currentState;
public State getCurrentState() {
return currentState;
}
public void setCurrentState(State state) {
this.currentState = state;
this.currentState.setContext(this);
}
public void handle1() {
this.currentState.handle1();
}
public void handle2() {
this.currentState.hanle2();
}
}
/**
* 測試類
*/
public class StateTest {
@Test
public void test() {
Context context = new Context();
context.setCurrentState(new State1());
context.handle1();
context.handle2();
context.handle2();
context.handle1();
}
}
優點:
- 結構清晰
- 封裝性好
- 遵循設計原則
缺點:
- 類數量比較多
適用場景:
- 行為隨狀態改變而改變的場景
- 條件、分支判斷語句的替代者
21享元模式
定義:
使用共用對象可有效地支持大量的細粒度的對象
示例:
/**
* 抽象享元角色
*/
public abstract class FlyWeight {
private String intrinsic;
protected final String extrinsic;
public FlyWeight(String extrinsic) {
this.extrinsic = extrinsic;
}
public abstract void operate(); // 業務邏輯
public String getIntrinsic() {
return intrinsic;
}
public void setIntrinsic(String intrinsic) {
this.intrinsic = intrinsic;
}
}
/**
* 具體享元角色
*/
public class ConcreteFlyWeight extends FlyWeight {
public ConcreteFlyWeight(String extrinsic) {
super(extrinsic);
}
public void operate() {
System.out.println("業務邏輯");
}
}
/**
* 享元對象工廠
*/
public class FlyWeightFactory {
private static HashMap<String, FlyWeight> flyWeightHashMap = new HashMap<String, FlyWeight>();
public static FlyWeight getFlyWeight(String extrinsic) {
FlyWeight flyWeight = null;
if (flyWeightHashMap.containsKey(extrinsic)) {
flyWeight = flyWeightHashMap.get(extrinsic);
} else {
flyWeight = new ConcreteFlyWeight(extrinsic);
flyWeightHashMap.put(extrinsic, flyWeight);
}
return flyWeight;
}
}
優點:
可以大大減少應用程式創建的對象,降低程式記憶體的占用,增強程式的性能
缺點:
提高了系統複雜性,需要分離出外部狀態和內部狀態
適用場景:
- 系統中存在大量相似的對象
- 需要緩衝池的場景
22橋梁模式
定義:
將抽象和實現解耦,使得兩者可以獨立地變化
示例:
/**
* 實現化角色
*/
public interface Implementor {
void doSomething();
void doAnyThing();
}
/**
* 具體實現化角色
*/
public class ConcreteImplementor implements Implementor{
public void doSomething() {
System.out.println("doSomething");
}
public void doAnyThing() {
System.out.println("doAnything");
}
}
/**
* 抽象化角色
*/
public abstract class Abstraction {
private Implementor implementor;
public Abstraction(Implementor implementor) {
this.implementor = implementor;
}
public void request() {
this.implementor.doSomething();
}
public Implementor getImplementor() {
return implementor;
}
}
/**
* 具體的抽象化角色
*/
public class ConcreteAbstraction extends Abstraction {
public ConcreteAbstraction(Implementor implementor) {
super(implementor);
}
@Override
public void request() {
super.request();
super.getImplementor().doAnyThing();
}
}
優點:
- 抽象和實現分離
- 優秀的擴充能力
- 實現細節對客戶透明
適用場景:
- 不希望或不適用使用繼承的場景
- 介面或抽象類不穩定的場景
- 重用性要求較高的場景