23种设计模式主要分为三大类:创建型模式、结构型模式和行为型模式。下面是这些设计模式的概览:
创建型模式(Creational Patterns)
- 单例模式(Singleton):确保一个类只有一个实例,并提供一个全局访问点。
- 工厂方法模式(Factory Method):定义一个用于创建对象的接口,但让子类决定实例化哪一个类。
- 抽象工厂模式(Abstract Factory):提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
- 建造者模式(Builder):将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
- 原型模式(Prototype):用原型实例指定创建对象的种类,并且通过拷贝这些原型来创建新的对象。
结构型模式(Structural Patterns)
-
适配器模式(Adapter) :将一个类的接口转换成客户希望的另一个接口。
-
MusicPlayerAdapter
类作为适配器,它实现了PlayMusicV2
接口,并在内部持有一个PlayMusicV1
对象的引用。当客户端调用play
方法时,适配器会根据传入的参数调用旧接口的playMp3
方法,从而实现了接口的转换,让旧的音频播放器库能够适应新的应用程序接口。例子:java// 原有的接口 interface PlayMusicV1 { void playMp3(String filename); } // 新的接口 interface PlayMusicV2 { void play(String audioType, String filename); } // 第三方库的实现类 class OldMusicPlayer implements PlayMusicV1 { @Override public void playMp3(String filename) { System.out.println("Playing MP3 file: " + filename); } } // 适配器类 class MusicPlayerAdapter implements PlayMusicV2 { private final PlayMusicV1 player; public MusicPlayerAdapter(PlayMusicV1 player) { this.player = player; } @Override public void play(String audioType, String filename) { if ("mp3".equalsIgnoreCase(audioType)) { player.playMp3(filename); } else { System.out.println("Unsupported audio type: " + audioType); } } } // 客户端代码 public class Client { public static void main(String[] args) { PlayMusicV1 oldPlayer = new OldMusicPlayer(); PlayMusicV2 adapter = new MusicPlayerAdapter(oldPlayer); // 使用新接口播放MP3 adapter.play("mp3", "favorite_song.mp3"); } }
-
-
桥接模式(Bridge):将抽象部分与它的实现部分分离,使它们都可以独立地变化。
-
组合模式(Composite):允许你将对象组合成树形结构来表示整体/部分层次结构。
-
装饰器模式(Decorator):动态地给一个对象添加一些额外的职责。
-
外观模式(Facade):为子系统中的一组接口提供一个一致的界面,简化接口。
-
享元模式(Flyweight):运用共享技术有效支持大量细粒度的对象。
-
代理模式(Proxy) :为其他对象提供一种代理以控制对这个对象的访问。
-
代理模式通过提供一个代理对象来控制对真实对象的访问,这个代理对象可以在访问真实对象前进行一些预处理操作,或者在访问后进行一些后续处理。下面是一个简单的Java代理模式示例,我们以租房为例,说明静态代理和动态代理的实现。
静态代理示例
首先定义一个租房服务的接口:
1public interface RentHouse { 2 void rent(); 3}
接着实现这个接口的真实对象(房东):
1public class RealEstate implements RentHouse { 2 @Override 3 public void rent() { 4 System.out.println("房东出租房子"); 5 } 6}
然后创建一个静态代理类,也实现相同的接口:
1public class HouseAgency implements RentHouse { 2 private RealEstate realEstate; 3 4 public HouseAgency(RealEstate realEstate) { 5 this.realEstate = realEstate; 6 } 7 8 @Override 9 public void rent() { 10 preProcess(); 11 realEstate.rent(); 12 postProcess(); 13 } 14 15 private void preProcess() { 16 System.out.println("中介进行房源展示、咨询等前期工作"); 17 } 18 19 private void postProcess() { 20 System.out.println("中介完成合同签订等后续工作"); 21 } 22}
最后是客户端代码,使用代理来租房:
1public class Client { 2 public static void main(String[] args) { 3 RealEstate realEstate = new RealEstate(); 4 RentHouse proxy = new HouseAgency(realEstate); 5 proxy.rent(); 6 } 7}
动态代理示例
动态代理通常使用Java的
java.lang.reflect.Proxy
类来创建代理对象,这里使用JDK动态代理为例:首先,保持
RentHouse
接口和RealEstate
类不变。动态代理类如下:
1import java.lang.reflect.InvocationHandler; 2import java.lang.reflect.Method; 3import java.lang.reflect.Proxy; 4 5public class DynamicProxy implements InvocationHandler { 6 private Object target; 7 8 public Object bind(Object target) { 9 this.target = target; 10 return Proxy.newProxyInstance(target.getClass().getClassLoader(), 11 target.getClass().getInterfaces(), this); 12 } 13 14 @Override 15 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 16 preProcess(); 17 Object result = method.invoke(target, args); 18 postProcess(); 19 return result; 20 } 21 22 private void preProcess() { 23 System.out.println("动态代理:中介进行房源展示、咨询等前期工作"); 24 } 25 26 private void postProcess() { 27 System.out.println("动态代理:中介完成合同签订等后续工作"); 28 } 29}
客户端代码调整为:
1public class Client { 2 public static void main(String[] args) { 3 RealEstate realEstate = new RealEstate(); 4 DynamicProxy dynamicProxy = new DynamicProxy(); 5 RentHouse proxy = (RentHouse) dynamicProxy.bind(realEstate); 6 proxy.rent(); 7 } 8}
在这个例子中,无论是静态代理还是动态代理,代理对象都在真实对象执行租房操作前后增加了额外的行为,如房源展示、合同签订等,体现了代理模式的价值。动态代理相比静态代理更加灵活,可以在运行时动态创建代理对象,无需为每个真实对象都手动创建一个代理类。
-
行为型模式(Behavioral Patterns)
-
责任链模式(Chain of Responsibility):使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。
-
命令模式(Command):将请求封装成对象,以便使用不同的请求、队列或者日志来参数化其他对象。
-
解释器模式(Interpreter):给定一种语言,定义它的文法和一个解释器,该解释器使用该语法来解释语言中的句子。
-
迭代器模式(Iterator):提供一种方法顺序访问一个聚合对象中各个元素,而又不需要暴露该对象的内部表示。
-
中介者模式(Mediator):用一个中介者对象来封装一系列的对象交互。
-
备忘录模式(Memento):在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。
-
观察者模式(Observer) :定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知并自动更新。
-
观察者模式解决了什么问题?
-
对象间一对多的依赖关系:在软件系统中,当一个对象的状态发生变化时,其他依赖于它的对象也都需要得到通知并被自动更新。这种情况下,如果采用传统的直接调用方式,对象间的耦合度会非常高,不利于代码的维护和扩展。观察者模式通过引入"主题"和"观察者"的概念,使得对象间的依赖关系变得清晰且灵活。
-
解耦:通过将依赖关系抽象为主题和观察者之间的订阅/发布关系,观察者模式降低了对象之间的耦合度。这使得代码更加易于维护和扩展,因为你可以在不修改主题对象代码的情况下,增加或删除观察者对象。
-
异步通信:观察者模式支持异步通信,即主题对象在状态改变时,可以立即通知观察者,而观察者可以在稍后的时间处理这个通知。这种异步通信的方式可以提高系统的响应速度,减少不必要的等待时间。
-
广播通知 :当主题对象的状态发生变化时,它可以通过调用
notifyObservers()
方法一次性通知所有注册的观察者对象,而不需要分别调用每个观察者的更新方法。这种广播通知的方式大大提高了代码的效率和可维护性。 -
支持动态关联 :观察者模式允许在运行时动态地建立或断开对象之间的依赖关系。这意味着你可以在程序运行的任何时刻,通过调用
registerObserver()
或removeObserver()
方法来添加或删除观察者对象。 -
事件驱动:观察者模式是一种典型的事件驱动编程模式。在这种模式下,程序的执行流程不是由调用顺序来控制的,而是由事件来驱动的。当某个事件发生时(例如主题对象的状态变化),与该事件相关的所有代码(即观察者的更新方法)都会被自动执行。这种编程方式使得代码更加清晰、易于理解和维护。
-
-
代码案例:
java// 主题接口 interface Subject { void registerObserver(Observer o); void removeObserver(Observer o); void notifyObservers(); // 假设这里有一个状态值 void setState(String state); String getState(); } // 观察者接口 interface Observer { void update(String state); } // 具体主题类 class ConcreteSubject implements Subject { private List<Observer> observers; private String state; public ConcreteSubject() { observers = new ArrayList<>(); } @Override public void registerObserver(Observer o) { observers.add(o); } @Override public void removeObserver(Observer o) { int i = observers.indexOf(o); if (i >= 0) { observers.remove(i); } } @Override public void notifyObservers() { for (Observer observer : observers) { observer.update(state); } } @Override public void setState(String state) { this.state = state; // 每当状态变化,就通知所有的观察者 notifyObservers(); } @Override public String getState() { return state; } } // 具体观察者类 class ConcreteObserver implements Observer { private String name; private Subject subject; public ConcreteObserver(String name, Subject subject) { this.name = name; this.subject = subject; // 初始化时注册自己为观察者 this.subject.registerObserver(this); } @Override public void update(String state) { System.out.println(name + " observed state change to: " + state); } } // 使用示例 public class ObserverPatternDemo { public static void main(String[] args) { Subject subject = new ConcreteSubject(); Observer observer1 = new ConcreteObserver("Observer 1", subject); Observer observer2 = new ConcreteObserver("Observer 2", subject); // 改变主题状态 subject.setState("New state"); // 移除一个观察者 subject.removeObserver(observer1); // 再次改变主题状态 subject.setState("Another new state"); } }
-
或者我们可以用Rxjava框架来写:
javaimport com.sun.media.jfxmediaimpl.MediaDisposer; import io.reactivex.rxjava3.annotations.NonNull; import io.reactivex.rxjava3.core.Observable; import io.reactivex.rxjava3.core.Observer; import io.reactivex.rxjava3.disposables.Disposable; public class ResultTest { public static void main(String[] args) { // 创建一个Observable(被观察者),它发射从1到5的整数序列 Observable<Integer> observable = Observable.just(1, 2, 3, 4, 5); // 创建一个Observer(观察者),它订阅Observable并处理接收到的数据 Observer<Integer> observer = new Observer<Integer>() { @Override public void onSubscribe(@NonNull Disposable d) { System.out.println("开始订阅数据..."); } @Override public void onNext(Integer value) { System.out.println("接收到数据:" + value); } @Override public void onError(Throwable e) { System.out.println("数据接收出错:" + e.getMessage()); } @Override public void onComplete() { System.out.println("数据接收完毕!"); } }; // Observer订阅Observable observable.subscribe(observer); } }
-
-
-
状态模式(State):允许一个对象在其内部状态改变时改变它的行为。
-
策略模式(Strategy):定义一系列算法,把它们一个个封装起来,并使它们可以相互替换。
-
模板方法模式(Template Method):定义一个操作中的算法骨架,而将一些步骤延迟到子类中实现。
-
访问者模式(Visitor):表示一个作用于某对象结构中的各元素的操作。