模板方法
模板方法模式,它在一個抽象類中定義了一個算法(業務邏輯)的骨架,具體步驟的實現由子類提供,它通過將算法的不變部分放在抽象類中,可變部分放在子類中,達到代碼復用和擴展的目的。
- 復用: 所有子類可以直接復用父類提供的模板方法,即上面提到的不變的部分。
- 擴展: 子類可以通過模板定義的一些擴展點就行不同的定制化實現。
我們來看一下示例代碼就很清晰了
abstract class AbstractClass { //模板類// 模板方法public final void templateMethod() {primitiveOperation1();primitiveOperation2();hook();}// 基本操作(抽象方法)protected abstract void primitiveOperation1();protected abstract void primitiveOperation2();// 鉤子方法(可選的操作,提供默認實現)protected void hook() {}
}
class ConcreteClassA extends AbstractClass { //具體方法A@Overrideprotected void primitiveOperation1() {System.out.println("ConcreteClassA: primitiveOperation1");}@Overrideprotected void primitiveOperation2() {System.out.println("ConcreteClassA: primitiveOperation2");}
}
class ConcreteClassB extends AbstractClass { //具體方法B@Overrideprotected void primitiveOperation1() {System.out.println("ConcreteClassB: primitiveOperation1");}@Overrideprotected void primitiveOperation2() {System.out.println("ConcreteClassB: primitiveOperation2");}@Overrideprotected void hook() {System.out.println("ConcreteClassB: hook");}
}
場景
應用場景:
例如支付場景,需要有支付寶、銀行卡、微信支付多種方式,可以定義支付的骨架,骨架里包括一些獲取訂單、驗證密碼、執行支付、發送短信的邏輯,像驗證密碼這些公共的就由骨架來提供,執行支付就由子類來獨立實現。
除此之外,支付場景,可能也會涉及到工廠模式,不同的支付方式,利用工廠模式不同的支付對象,再調用支付對象實現不同的功能。
策略模式
策略模式是一種行為型設計模式,提供一個策略接口,并獨立實現各種具體策略,允許在運行時動態選擇具體策略,從而實現更加靈活的代碼結構。該模式的中心不是如何實現算法,而是如何組織、調用這些算法,方便調用方在針對不同場景靈活切換不同的策略。
// 策略的定義
public interface Strategy {void execute(User user);
}
//具體策略A
public class AStrategy implements Strategy{void execute(User user) {System.out.println("Executing Strategy A " + user.getName());}
}
//具體策略B
public class BStrategy implements Strategy{void execute(User user) {System.out.println("Executing Strategy B " + user.getName());}
}// 策略集合的創建
public class StrategyFactory {private static final Map<String, Strategy> strategies = new HashMap<>();static {strategies.put("A", new AStrategy());strategies.put("B", new BStrategy());}public static Strategy getStrategy(OrderType type) {return strategies.get(type);}
}// 策略的使用
public class xxService {public void execute(User user) {String type = user.getType();Strategy strategy = StrategyFactory.getStrategy(type);return strategy.execute(user);}
}
可以看到,上述的代碼通過使用策略模式,可以將不同的算法策略封裝起來,并根據 user 的類型在運行時動態選擇具體的算法策略,從而提高系統的靈活性和可擴展性。
可以用在例如多種支付方式的切換、不同排序算法的切換等多策略實現的場景。
責任鏈模式
責任鏈模式允許將多個對象連接成一條鏈,并且沿著這條鏈傳遞請求,讓多個對象都有機會處理這個請求,請求會順著鏈傳遞,直到某個對象處理它為止。
在很多場景都能看到責任鏈模式,比如日志的處理,不同級別不同輸出。再比如 Spring 過濾器的 Chain 也是責任鏈模式。
下面是一個簡單的日志處理示例代碼。
- 日志處理器有三種類型: 控制臺日志處理器(ConsoleLogger)、文件日志處理器(FileLogger)、錯誤日志處理器(ErrorLogger)。
- 日志處理器按照優先級進行處理,如果當前處理器不能處理,則將請求傳遞給下一個處理器。
// 責任鏈模式的抽象日志類
abstract class Logger {public static int INFO = 1;public static int DEBUG = 2;public static int ERROR = 3;protected int level;protected Logger nextLogger; //日志中可以存下一個日志public void setNextLogger(Logger nextLogger) {this.nextLogger = nextLogger;}public void logMessage(int level, String message) {if (this.level <= level) {write(message);}if (nextLogger != null) {nextLogger.logMessage(level, message);}}protected abstract void write(String message);
}// 具體處理器類:控制臺日志處理器
class ConsoleLogger extends Logger {public ConsoleLogger(int level) {this.level = level;}@Overrideprotected void write(String message) {System.out.println("Standard Console::Logger: " + message);}
}
// 具體處理器類:文件日志處理器
class FileLogger extends Logger {public FileLogger(int level) {this.level = level;}@Overrideprotected void write(String message) {System.out.println("File::Logger: " + message);}
}
// 具體處理器類:錯誤日志處理器
class ErrorLogger extends Logger {public ErrorLogger(int level) {this.level = level;}@Overrideprotected void write(String message) {System.out.println("Error Console::Logger: " + message);}
}// 客戶端代碼
public class ChainPatternDemo {private static Logger getChainOfLoggers() { //獲取日志責任鏈Logger errorLogger = new ErrorLogger(Logger.ERROR);Logger fileLogger = new FileLogger(Logger.DEBUG);Logger consoleLogger = new ConsoleLogger(Logger.INFO);errorLogger.setNextLogger(fileLogger); //存下一個日志fileLogger.setNextLogger(consoleLogger); //存再下一個日志return errorLogger; //最后返回}public static void main(String[] args) {Logger loggerChain = getChainOfLoggers();loggerChain.logMessage(Logger.INFO, "mianshiya.com");loggerChain.logMessage(Logger.DEBUG, "小程序:面試鴨");loggerChain.logMessage(Logger.ERROR, "網頁端:mianshiya.com");}
}
觀察者模式
觀察者模式其實也稱為發布訂閱模式,它定義了對象之間的一種一對多的依賴關系,讓多個觀察者對象 同時監聽某一個主題對象。當主題對象狀態發生變化時,它會通知所有觀察者對象。
發布訂閱與觀察者模式的區別**:發布訂閱引入了第三方組件來維護發布者和訂閱者的關系,他們之間不直接通信,做到了真正解耦。**
觀察者模式的組成部分
- Subject(主題/被觀察者): 狀態發生變化時,通知所有注冊的觀察者
- Observer(觀察者): 接收來自主題的更新通知,并進行相應的操作。
- ConcreteSubject(具體主題): 實現具體的主題對象,保存需要被觀察的狀態。
- ConcreteObserver(具體觀察者): 實現具體的觀察者對象,更新自己以與主題的狀態同步
我們以一個新聞發布系統來理解下觀察者模式。
1)定義接口
? 新聞發布者(Subject)可以發布新聞,觀察者(Observer)是訂閱者,希望獲取新聞更新
2)實現具體類
- 具體新聞發布者(Concrete Subject)維護一個訂閱者列表,并在發布新聞時通知他們
- 具體觀察者(Concrete Observer)如報紙、新聞網站等,實現更新方法來展示新聞
3)訂閱和退訂
? 訂閱者可以訂閱(注冊)或退訂(注銷)新聞發布者的新聞。
4)發布新聞
? 當新聞發布者有新新聞時,"觀察者更新自己的新聞內容。它通知所有訂閱的觀察者
5)客戶端代碼
? 客戶端代碼創建新聞發布者和觀察者對象,訂閱者選擇訂新聞,并在接收到新聞時更新顯示。
// 定義觀察者接口
interface Observer {void update(String news);
}
// 定義主題接口
interface Subject {void attach(Observer o);void detach(Observer o);void notifyObservers();
}
// 具體新聞發布者
class NewsPublisher implements Subject {private List<Observer> observers = new ArrayList<>();public void attach(Observer o) {observers.add(o);}public void detach(Observer o) {observers.remove(o);}public void notifyObservers() {for (Observer observer : observers) {observer.update("新聞更新");}}public void publishNews() {// 假設這里是新聞發布邏輯,會通知發布者里面集合的觀察者notifyObservers();}
}// 具體觀察者
class NewsPaper implements Observer {public void update(String news) {System.out.println("新報 " + news);}
}// 客戶端代碼
public class Client {public static void main(String[] args) {NewsPublisher publisher = new NewsPublisher(); //發布者Observer newsPaper = new NewsPaper(); //觀察者publisher.attach(newsPaper); //導入觀察者publisher.publishNews(); //發布新聞publisher.detach(newsPaper); //移除觀察者publisher.publishNews(); // 此時報紙訂閱者不會接收到新聞}
}
例如消息隊列、 Spring 內的監聽器機制等都是其應用場景
迭代器模式
迭代器模式,它提供一種方法順序訪問一個集合對象中的各個元素,而又不暴露該對象(可能是數組、鏈表、樹等等)的內部實現。
將遍歷邏輯與集合對象的實現分離,提供一致的遍歷方式,使得代碼統一化,在不改變遍歷代碼的情況下就能替換底層集合實現。
像 Java 的java.util.Iterator
接口和Iterable
接口是迭代器模式的直接應用。所有集合類(如ArrayList、HashSet、LinkedList 等)都實現了 Iterable 接口,并提供了 iterator() 方法來獲取迭代器,例如,遍歷 ArrayList 中的元素:
List<String> list = new ArrayList<>();
list.add("one");
list.add("two");
list.add("three");Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {System.out.println(iterator.next());
}
再比如 JDBC 的ResultSet
接口遍歷數據庫査詢結果,也是迭代器模式的實現。
迭代器模式的組成部分
- lterator(迭代器接口): 定義訪問和歷元素的接口
- Aggregate(聚合接口): 定義創建迭代器的接口
- Concretelterator(具體迭代器): 實現迭代器接口,負責遍歷聚合對象中的元素
- ConcreteAggregate(具體聚合類): 實現聚合接口,返回一個具體的迭代器實例。
我們來簡單實現一個迭代器
//定義迭代器接口
interface Iterator<T> {boolean hasNext();T next();
}
//定義聚合接口
interface Aggregate<T> {Iterator<T> createIterator();
}//定義具體迭代器
class ConcreteIterator<T> implements Iterator<T> {private List<T> items;private int position = 0; //當前遍歷位置public ConcreteIterator(List<T> items) {this.items = items;}@Overridepublic boolean hasNext() { //如果進行集合遍歷時位置<集合大小,則返回truereturn position < items.size();}@Overridepublic T next() { //獲取當前遍歷位置的下一個元素return items.get(position++);}
}
//定義具體聚合類
class ConcreteAggregate<T> implements Aggregate<T> {private List<T> items = new ArrayList<>();public void addItem(T item) { //添加元素方法items.add(item);}@Overridepublic Iterator<T> createIterator() {return new ConcreteIterator<>(items);}
}
//簡單使用
public class Client {public static void main(String[] args) {ConcreteAggregate<String> aggregate = new ConcreteAggregate<>();aggregate.addItem("Item 1"); //添加集合元素aggregate.addItem("Item 2");aggregate.addItem("Item 3");Iterator<String> iterator = aggregate.createIterator(); //返回對應該集合的迭代器while (iterator.hasNext()) {System.out.println(iterator.next());}}
}
命令模式
命令模式(Command Pattern)是一種行為設計模式,它將請求封裝成對象,使得請求參數化,便于對請求排隊或記錄請求日志,以及支持可撤銷的操作。
簡單看下以開關燈為例實現命令模式的代碼,更容易理解上面那段含義:
// 命令接口
interface Command {void execute();
}
// 接收者
class Light { public void on() { //用于控制燈光的開關System.out.println("The light is on.");}public void off() {System.out.println("The light is off.");}
}// 具體命令 開燈
class LightOnCommand implements Command {private Light light;public LightOnCommand(Light light) {this.light = light;}@Overridepublic void execute() {light.on();}
}
// 具體命令 關燈
class LightOffCommand implements Command {private Light light;public LightOffCommand(Light light) {this.light = light;}@Overridepublic void execute() {light.off();}
}
// 調用者,傳入了開關命令,因此可以開關燈
class RemoteControl {private Command command;public void setCommand(Command command) {this.command = command;}public void pressButton() {command.execute();}
}// 客戶端代碼
public class Client {public static void main(String[] args) {Light light = new Light(); Command lightOn = new LightOnCommand(light); //開燈Command lightOff = new LightOffCommand(light); //關燈RemoteControl remote = new RemoteControl();remote.setCommand(lightOn); remote.pressButton(); //開燈remote.setCommand(lightOff);remote.pressButton(); //關燈}
}
狀態模式
狀態模式 允許對象在其內部狀態發生改變時改變其行為,將狀態的行為封裝在獨立的類中,并將這些狀態對象組合在擁有狀態的對象中,這樣就可以在狀態改變時切換狀態對象,從而改變對象的行為
它主要是狀態機的一種實現方式,狀態機可分為: 狀態、事件、動作三個部分。事件的觸發就會導致狀態的改變,并且可作出一定的動作(也可以沒有動作,只有狀態的改變)
// 定義狀態接口
interface State {void handle(Context context);
}
// 上下文環境,狀態模式的核心,它持有一個 State 對象,并提供了 setState 方法來改變當前狀態
class Context {private State state;public Context(State state) {this.state = state;}public void setState(State state) {this.state = state;}public void request() {state.handle(this);}
}
// 具體實現類
//AB每個類在處理請求時都會打印一條消息,并且會改變上下文 Context 的狀態為另一種狀態
class ConcreteStateA implements State {@Overridepublic void handle(Context context) {System.out.println("State A is handling the request.");context.setState(new ConcreteStateB());}
}
class ConcreteStateB implements State {@Overridepublic void handle(Context context) {System.out.println("State B is handling the request.");context.setState(new ConcreteStateA());}
}public class Client {public static void main(String[] args) {Context context = new Context(new ConcreteStateA()); //新建A狀態環境context.request(); // State A is handling the request. //請求A狀態,然后設置當前狀態為Bcontext.request(); // State B is handling the request.context.request(); // State A is handling the request.context.request(); // State B is handling the request.}
}
中介者模式
中介模式通過引入了一個中介對象,來封裝一組對象之間的交互,來避免對象之間的直接交互。通過引入一個中介者對象,使對象之間的關系變得簡單且易于維護。
聽起來不就是和現實生活中的中介一樣嘛。
引入中介模式的原因:多對象交互可能會使得關系圖很混亂,代碼也不清晰,讓多對象都和中介交互就能避免這點

聊天室實現其實就是運用了中介模式。信息的傳遞都由服務器這個中介來做,所有用戶都把消息發給服務器,不然如果點對點傳輸大家可以想象下有多復雜。我們可以簡單的看下聊天室的實現:
interface ChatMediator { //中介者接口void sendMessage(String message, User user);void addUser(User user);
}
class ChatMediatorImpl implements ChatMediator { //具體中介者private List<User> users; //存儲加入的用戶public ChatMediatorImpl() {this.users = new ArrayList<>();}@Overridepublic void addUser(User user) {this.users.add(user);}@Overridepublic void sendMessage(String message, User user) {for (User u : this.users) {if (u != user) {u.receive(message);}}}
}abstract class User { //抽象用戶protected ChatMediator mediator;protected String name;public User(ChatMediator mediator, String name) {this.mediator = mediator;this.name = name;}public abstract void send(String message);public abstract void receive(String message);
}class UserImpl extends User { //用戶public UserImpl(ChatMediator mediator, String name) {super(mediator, name);}@Overridepublic void send(String message) {System.out.println(this.name + " sends: " + message);mediator.sendMessage(message, this);}@Overridepublic void receive(String message) {System.out.println(this.name + " receives: " + message);}
}
public class ChatClient { //客戶端public static void main(String[] args) {ChatMediator mediator = new ChatMediatorImpl();User user1 = new UserImpl(mediator, "Alice");User user2 = new UserImpl(mediator, "Bob");User user3 = new UserImpl(mediator, "Charlie");mediator.addUser(user1); //用戶加入中介mediator.addUser(user2);mediator.addUser(user3);user1.send("Hello, everyone!");}
}
訪問者模式
訪問者模式它將數據結構與操作分離,使得你可以在不改變數據結構的前提下定義新的操作。訪問者模式通過將操作封裝到獨立的訪問者對象中,使得新的操作可以很容易地添加到系統中。
例如對象序列化場景,比如需要將對象轉成 JSON 或者 XML 等格式,利用訪問者模式將對象的結構和序列化操作分離,這樣就能很方便的擴展新的序列化格式。先看下示例代碼:
//訪問者接口(類比序列化接口)
interface Visitor {void visit(ElementA element);void visit(ElementB element);
}
//具體訪問者類(類比具體序列化實現)
class ConcreteVisitor implements Visitor {@Overridepublic void visit(ElementA element) {System.out.println("Processing ElementA: " + element.getName());}@Overridepublic void visit(ElementB element) {System.out.println("Processing ElementB: " + element.getName());}
}//需要對序列化的元素接口
interface Element {void accept(Visitor visitor);
}// 具體被序列化的元素A
class ElementA implements Element {private String name;public ElementA(String name) {this.name = name;}public String getName() {return name;}@Overridepublic void accept(Visitor visitor) {visitor.visit(this);}
}
// 具體被序列化的元素B
class ElementB implements Element {private String name;public ElementB(String name) {this.name = name;}public String getName() {return name;}@Overridepublic void accept(Visitor visitor) {visitor.visit(this);}
}//對象結構,包含很多元素
class ObjectStructure {private List<Element> elements = new ArrayList<>();public void addElement(Element element) {elements.add(element);}public void accept(Visitor visitor) {for (Element element : elements) {element.accept(visitor);}}
}// 客戶端代碼
public class Client {public static void main(String[] args) {// 組裝對象ObjectStructure objectStructure = new ObjectStructure();objectStructure.addElement(new ElementA("Element A1"));objectStructure.addElement(new ElementB("Element B1"));objectStructure.addElement(new ElementA("Element A2"));//訪問者 (如果是序列化場景,ConcreteVisitor 可以當做 JSON 序列化)Visitor visitor = new ConcreteVisitor();objectStructure.accept(visitor);// 假設后面要替換 xml 序列化,僅需新建 xml 的訪問者,然后傳入到對象內部即可Visitor visitorXML = new XMLVisitor();objectStructure.accept(visitorXML);}
}
可以看到,將數據結構和操作分離開之后,如果要替換具體的操作,僅需新增一個操作即可,不需要修改任何數據結構,這就符合開閉原則,也保證了類的職責單一。
備忘錄模式
備忘錄模式指的是在不違背封裝原則的前提下,捕獲對象內部的狀態,將其保存在外部,便于后面對象恢復之前的狀態,使得系統更具靈活性和可維護性。
聽來像不像保留個快照作為備份,后面基于這個快照進行恢復? 所以備忘錄模式其實也叫快照模式。主要用于撤銷、恢復等場景。
來看下示例代碼,我先大致解釋一下幾個類的含義:
- Memento,存儲狀態的備忘錄
- Originator,需要備份狀態的對象類
- Caretaker,管理者,僅保存備忘錄
// 備忘錄類
class Memento {private String state;public Memento(String state) {this.state = state;}public String getState() {return state;}
}// 需要備份狀態的對象類
class Originator {private String state;public void setState(String state) {this.state = state;System.out.println("State set to: " + state);}public String getState() {return state;}public Memento createMemento() {return new Memento(state);}public void restoreMemento(Memento memento) {this.state = memento.getState();System.out.println("State restored to: " + state);}
}
//管理存儲類
class Caretaker {private Memento memento;public Memento getMemento() {return memento;}public void setMemento(Memento memento) {this.memento = memento;}
}//客戶端類
public class Client {public static void main(String[] args) {Originator originator = new Originator(); //對象類Caretaker caretaker = new Caretaker(); //存儲類originator.setState("State1");caretaker.setMemento(originator.createMemento()); //保存快照originator.setState("State2");System.out.println("Current State: " + originator.getState());originator.restoreMemento(caretaker.getMemento()); //恢復快照System.out.println("Restored State: " + originator.getState());}
}
如果直接利用 set 方法修改內部狀態,就違反了封裝的原則,因為如果你暴露了 set 方法,則對象內部的狀態很可能被別的業務調用修改了。而 restoreMemento 是一個很清晰的方法定義,即恢復之前的狀態,不會被亂用。這也是備忘錄模式的前提,不違反封裝原則。