文章目录
设计模式概述
设计模式的定义:
- 一套被反复使用的、多数人知晓的、经过分类编目的、代码设计经验的总结
- 目的:为了可重用代码、让代码更容易被他人理解、提高代码的可靠性
- 是一种用于对软件系统中不断重现的设计问题的解决方案进行文档化的技术
- 是一种共享专家设计经验的技术
GOF的定义: 设计模式是在特定环境下为解决某一通用软件设计问题提供的一套定制的解决方案,该方案描述了对象和类之间的相互作用。
设计模式的基本要素:
- 模式名称
- 问题
- 解决方案
- 效果
设计模式的分类:
根据目的分类:
- 创建型模式:主要用于创建对象
- 结构型模式:主要用于处理类或对象的组合
- 行为型模式:主要用于对描述类或对象如何交互和怎样分配职责
根据范围分类(模式主要是处理类之间的关系还是处理对象之间的关系):
- 类模式:处理类和子类之间的关系;这些关系通过继承建立,是一种静态关系
- 对象模式:处理对象间的关系;更具有动态性
常见的设计模式:
范围\目的 | 创建型模式 | 结构型模式 | 行为型模式 |
---|---|---|---|
类模式 | 工厂方法模式 | (类)适配器模式 | 解释器模式、模板方法模式 |
对象模式 | 抽象工厂、建造者、原型、单例 | (对象)适配器、桥接、组合、装饰、外观、享元、代理 | 职责链、命令、迭代器、中介者、备忘录、观察者、状态、策略、访问者 |
面向对象设计原则
模式名称 | 定义 |
---|---|
单一职责原则SRP | 一个对象应该只包含单一的职责,并且该职责被完整的封装在一个类中 |
开闭原则OCP | 软件实体应当对扩展开放,都修改关闭 |
里氏代换原则LSP | 所有引用基类的地方必须能透明的使用其子类的对象 |
依赖倒转原则DIP | 高层模块不应该依赖底层模块,他们都应该依赖抽象。抽线不应该依赖于细节,细节应该依赖抽象 |
接口隔离原则ISP | 客户端不应该依赖那些它不需要的接口 |
合成复用原则CRP | 优先使用对象组合,而不是继承来达到复用 |
迪米特法则LoD | 每个软件单位对其他的单位都只有最少的知识,而对局限于那些与本单位密切相关的软件单元 |
创建型设计模式(重点)
模式名称 | 定义 |
---|---|
简单工厂模式 | 定义一个工厂类,它可以根据参数的不同返回不同类的实例,被创建的实例通常都具有共同的父类 |
工厂方法模式 | 定义一个用于创建对象的接口,但是让子类决定将哪一个类实例化。工厂方法模式让一个类的实例化延迟到其子类 |
抽象工厂模式 | 提供一个创建一系列相关或相互依赖对象的接口,而无须指定它们具体的类 |
建造者模式 | 将一个复杂对象的构建与它的表示分离。使得同样构建过程可以创建不同的表达 |
原型模式 | 使用原型实例制定待创建对象的类型,并且通过复制这个原型来创建新的对象 |
单例模式 | 确保一个类只有一个实例,并提供一个全局访问点来访问这个唯一实例 |
简单工厂模式
简单工厂模式结构:
- Factory(工厂角色)
- Product(抽象产品角色)
- ConcreteProduct(具体产品角色)
抽象产品类
public abstract class Product {
//所有产品类的公共业务方法
public void methodSame() {
//公共方法的实现
}
//声明抽象业务方法
public abstract void methodDiff();
}
具体产品类
public class ConcreteProduct extends Product{
//实现业务方法
public void methodDiff() {
//业务方法的实现
}
}
工厂类
public class Factory {
//静态工厂方法
public static Product getProduct(String arg) {
Product product = null;
if (arg.equalslgnoreCase("A")){
product = new ConcreteProductA();
//初始化设置product
}
else if (arg.equalslgnoreCase("B"")){
product = new ConcreteProductB();
//初始化设置product
}
return product;
}
}
客户类
public class Client {
public static void main(String args[]) {
Product product;
product = Factory.getProduct("A");
//通过工厂类创建产品对
product.methodSame();
product.methodDiff();
}
}
优点:
- 实现了对象创建和使用的分离
- 客户端无须知道所创建的具体产品类的类名,只需要知道具体产品类所对应的参数即可
- 通过引入配置文件,可以在不修改任何客户端代码的情况下更换和增加新的具体产品类,在一定程度上提高了系统的灵活性
缺点:
- 工厂类集中了所有产品的创建逻辑,职责过重,一旦不能正常工作,整个系统都要受到影响
- 增加系统中类的个数(引入了新的工厂类),增加了系统的复杂度和理解难度
- 系统扩展困难,一旦添加新产品不得不修改工厂逻辑
- 由于使用了静态工厂方法,造成工厂角色无法形成基于继承的等级结构,工厂类不能得到很好地扩展
适用环境:
- 工厂类负责创建的对象比较少,由于创建的对象较少,不会造成工厂方法中的业务逻辑太过复杂
- 客户端只知道传入工厂类的参数,对于如何创建对象并不关心扩展
工厂方法模式
工厂方法模式结构:
- Product(抽象产品)
- ConcreteProduct(具体产品)
- Factory(抽象工厂)
- ConcreteProduct(具体工厂)
抽象工厂类
public interface Factory {
public Product factoryMethod();
}
具体工厂类
public class ConcreteFactory implements Factory {
public Product factoryMethod() {
return new ConcreteProduct();
}
}
客户端代码
......
Factory factory;
factory = new ConcreteFactory();//可通过配置文件和反射机制实现Product product;
product = factory.factoryMethod();
......
优点:
- 工厂方法用来创建客户所需要的产品,同时还向客户隐藏了哪种具体产品类将被实例化这一细节
- 能够让工厂自主确定创建何种产品对象,而如何创建这个对象的细节则完全封装在具体工厂内部
- 在系统中加入新产品时,完全符合开闭原则
缺点:
- 系统中类的个数将成对增加,在一定程度上增加了系统的复杂度,会给系统带来一些额外的开销
- 增加了系统的抽象性和理解难度
适用环境:
- 客户端不知道它所需要的对象的类(客户端不需要知道具体产品类的类名,只需要知道所对应的工厂即可,具体产品对象由具体工厂类创建)
- 抽象工厂类通过其子类来指定创建哪个对象
抽象工厂模式
概念:
- 产品等级结构:产品等级结构即产品的继承结构
- 产品族:产品族是指由同一个工厂生产的,位于不同产品等级结构中的一组产品
- 当系统所提供的工厂生产的具体产品并不是一个简单的对象,而是多个位于不同产品等级结构、属于不同类型的具体产品时就可以使用抽象工厂模式
- 抽象工厂模式是所有形式的工厂模式中最为抽象和最具一般性的一种形式
工厂方法模式结构:
- AbstractFactory(抽象工厂)
- ConcreteFactory(具体工厂)
- AbstractProduct(抽象产品)
- ConcreteProduct(具体产品)
抽象工厂类
public interface AbstractFactory {
public AbstractProductA createProductA();//工厂方法一
public AbstractProductB createProductB();//工厂方法二
......
}
具体工厂类
public class ConcreteFactory1 implements AbstractFactory {
//工厂方法一
public AbstractProductA createProductA() {
return new ConcreteProductA1();
}
//工厂方法二
public AbstractProductB createProductB(){
return new ConcreteProductB1();
}
优点:
- 隔离了具体类的生成,使得客户端并不需要知道什么被创建
- 当一个产品族中的多个对象被设计成一起工作时,它能够保证客户端始终只使用同一个产品族中的对象
- 增加新的产品族很方便,无须修改已有系统,符合开闭原则
缺点:
- 增加新的产品等级结构麻烦,需要对原有系统进行较大的修改,甚至需要修改抽象层代码,这显然会带来较大的不便,违背了开闭原则
适用环境:
- 一个系统不应当依赖于产品类实例如何被创建、组合和表达的细节
- 系统中有多于一个的产品族,但每次只使用其中某一产品族
- 属于同一个产品族的产品将在一起使用,这一约束必须在系统的设计中体现出来
- 产品等级结构稳定,在设计完成之后不会向系统中增加新的产品等级结构或者删除已有的产品等级结构
单例模式
单例模式结构:
- Singleton(单例)
/*要求:
私有构造函数
静态私有成员变量(自身类型)
静态公有的工厂方法
*/
public class Singleton {
private static singleton instance=null;//静态私有成员变量
//私有构造函数
private Singleton() {}
// 静态公有工厂方法,返回唯一实例
public static singleton getlnstance() {
if( instance==null)
instance=new Singleton();return instance;
}
}
饿汉式单例(Eager Singleton)
public class EagerSingleton {
private static final EagerSingleton instance = new EagerSingletonO);
private EagerSingleton( {}
public static EagerSingleton getInstance(){
return instance;
}
}
懒汉式单例(Lazy Singleton)
//延时加载(Lazy Load)基础版
public class LazySingleton {
private static LazySingleton instance = null;
private LazySingleton() { }
public static LazySingleton getInstance() {
if (instance == null) {
instance = new LazySingleton();
}
return instance;
}
}
-------------------------------------------------------
改进版--锁方法
public class LazySingleton {
private static LazySingleton instance = null;
private LazySingleton() { }
synchronized public static LazySingleton getInstance() {
if (instance == null) {
instance = new LazySingleton();
}
return instance;
}
}
-------------------------------------------------------
锁代码段
……
public static LazySingleton getInstance() {
if (instance == null) {
synchronized (LazySingleton.class) {
instance = new LazySingleton();
}
}
return instance;
}
……
-------------------------------------------------------
双重检查锁定
public class LazySingleton {
private volatile static LazySingleton instance = null;
private LazySingleton() { }
public static LazySingleton getInstance() {
//第一重判断
if (instance == null) {
//锁定代码块
synchronized (LazySingleton.class) {
//第二重判断
if (instance == null) {
instance = new LazySingleton(); //创建单例实例
}
}
}
return instance;
}
}
- 饿汉式单例类:无须考虑多个线程同时访问的问题;调用速度和反应时间优于懒汉式单例;资源利用效率不及懒汉式单例;系统加载时间可能会比较长
- 懒汉式单例类:实现了延迟加载;必须处理好多个线程同时访问的问题;需通过双重检查锁定等机制进行控制,将导致系统性能受到一定影响
优点:
- 提供了对唯一实例的受控访问
- 可以节约系统资源,提高系统的性能
- 允许可变数目的实例(多例类)
缺点:
- 扩展困难(缺少抽象层)
- 单例类的职责过重
- 由于自动垃圾回收机制,可能会导致共享的单例对象的状态丢失
适用环境:
- 系统只需要一个实例对象,或者因为资源消耗太大而只允许创建一个对象
- 客户调用类的单个实例只允许使用一个公共访问点,除了该公共访问点,不能通过其他途径访问该实例
结构性型设计模式(重点)
分类:
-
类结构型模式:关心类的组合,由多个类组合成一个更大的系统,在类结构型模式中一般只存在继承关系和实现关系
-
对象结构型模式:关心类与对象的组合,通过关联关系,在一个类中定义另一个类的实例对象,然后通过该对象调用相应的方法
模式名称 | 定义 |
---|---|
适配器模式 | 将一个类的接口转换成客户希望的另一个接口。适配器模式让那些接口不兼容的可以一起工作 |
桥接模式 | 将抽象部分与它实现部分解耦,使得两者都能够独立变化 |
组合模式 | 组合多个对象形成树形结构,以表示具有部分-整体关系的层次结构。组合模式让客户端可以统一对待单个对象和组合对象 |
装饰模式 | 动态的给一个对象增加一些额外的职责。就扩展功能而言,装饰模式提供了一种比使用子类更加灵活的替代方案 |
外观模式 | 为子系统中的一组接口提供一个统一的入口。外观模式定义了一个高层接口,这个接口使得这个子系统更加容易使用 |
享元模式 | 运用共享技术有效地支持大量细粒度对象的复用 |
代理模式 | 给某一个对象提供一个代理或占位符,并由代理对象来控制对原对象的访问 |
适配器模式
适配器模式结构(类适配器):
适配器模式结构(对象适配器):
- Target(目标抽象类)
- Adapter(适配器类)
- Adaptee(适配者类)
类适配器
public class Adapter extends Adaptee implements Target {
public void request() {
super.specificRequest();
}
}
对象适配器
public class Adapter extends Target {
private Adaptee adaptee;//维持一个对适配者对象的引用
public Adapter(Adaptee adaptee) {
this.adaptee=adaptee;
}
public void request() {
adaptee.specificRequest();
//转发调用
}
}
优点:
- 将目标类和适配者类解耦,通过引入一个适配器类来重用现有的适配者类,无须修改原有结构
- 增加了类的透明性和复用性,提高了适配者的复用性,同一个适配者类可以在多个不同的系统中复用
- 灵活性和扩展性非常好
- 类适配器模式:置换一些适配者的方法很方便
- 对象适配器模式:可以把多个不同的适配者适配到同一个目标,还可以适配一个适配者的子类
缺点:
1、类适配器模式:
- 一次最多只能适配一个适配者类,不能同时适配多个适配者·
- 配者类不能为最终类
- 目标抽象类只能为接口,不能为类
2、对象适配器模式:在适配器中置换适配者类的某些方法比较麻烦
适用环境:
- 系统需要使用一些现有的类(适配者),而这些类的接口不符合系统的需要,甚至没有这些类的源代码
- 创建一个可以重复使用的类(目标类/适配者),用于和一些彼此之间没有太大关联的类,包括一些可能在将来引进的类一起工作
桥接模式
桥接模式结构:
- Abstraction(抽象类)
- RefineAbstraction(扩充抽象类)
- Implementor(实现类接口)
- ConcreteImplementor(具体实现类)
类接口
public interface Implementor {
public void operationlmpl();
}
具体实现类
public class Concretelmplementor implements Implementor {
public void operationlmpl() {
//具体业务方法的实现
}
}
抽象类
public abstract class Abstraction {
protected lmplementor impl;//定义实现类接口对象
public void setlmpl(Implementor impl){
this.impl=impl;
}
public abstract void operation();//声明抽象业务方法
}
扩充抽象类(细化抽象类)
public class RefinedAbstraction extends Abstraction {
public void operation() {
//业务代码
impl.operationlmpl();//调用实现类的方法业务代码
}
桥接模式与适配器模式的联用
- 桥接模式:用于系统的初步设计,对于存在两个独立变化维度的类可以将其分为抽象化和实现化两个角色,使它们可以分别进行变化
- 适配器模式:当发现系统与已有类无法协同工作时
优点:
- 分离抽象接口及其实现部分
- 可以取代多层继承方案,极大地减少了子类的个数
- 提高了系统的可扩展性,在两个变化维度中任意扩展一个维度,不需要修改原有系统,符合开闭原则
缺点:
- 会增加系统的理解与设计难度,由于关联关系建立在抽象层,要求开发者开始就针对抽象层进行设计与编程
- 正确识别出系统中两个独立变化的维度并不是一件容易的事情
适用环境:
- 需要在抽象化和具体化之间增加更多的灵活性,避免在两个层次之间建立静态的继承关系
- 抽象部分和实现部分可以以继承的方式独立扩展而互不影响
- 一个类存在两个(或多个)独立变化的维度,且这两个(或多个)维度都需要独立地进行扩展
- 不希望使用继承或因为多层继承导致系统类的个数急剧增加的系统
组合模式
组合模式结构:
- Component(抽象构件)
- Leaf(叶子构件)
- Composite(容器构件)
抽象构件角色
public abstract class Component {
public abstract void add(Component c);//增加成员
public abstract void remove(Component c);//删除成员
public abstract Component getChild(int i);//获取成员
public abstract void operation();//业务方法
}
叶子构件角色
public class Leaf extends Component {
public void add(component c) {//异常处理或错误提示
}
public void remove(Component c) {//异常处理或错误提示
}
public component getChild(int i) {//异常处理或错误提示
return null;
}
public void operation() {
//叶子构件具体业务方法的实现
}
容器构件角色
public class Composite extends Component {
private ArrayList<Component> list = new ArrayList<Component>();
public void add(Component c){
list.add(c);
}
public void remove(Component c){
list.remove(c);
}
public Component getChild(int i) {
return (Component)list. get(i);
}
public void operation(){
//容器构件具体业务方法的实现,将递归调用成员构件的业务方法
for(Object obj:list) {
((component)obj).operation();
}
}
}
优点:
- 可以清楚地定义分层次的复杂对象,表示对象的全部或部分层次,让客户端忽略了层次的差异,方便对整个层次结构进行控制
- 客户端可以一致地使用一个组合结构或其中单个对象,不必关心处理的是单个对象还是整个组合结构,简化了客户端代码
- 增加新的容器构件和叶子构件都很方便,符合开闭原则
- 为树形结构的面向对象实现提供了一种灵活的解决方案
缺点:
- 在增加新构件时很难对容器中的构件类型进行限制
适用环境:
- 在具有整体和部分的层次结构中,希望通过一种方式忽略整体与部分的差异,客户端可以一致地对待它们
- 在一个使用面向对象语言开发的系统中需要处理一个树形结构
- 在一个系统中能够分离出叶子对象和容器对象,而且它们的类型不固定,需要增加一些新的类型
外观模式
外观模式结构:
- Facade(外观角色)
- SubSystem(子系统角色)
子系统类
public class SubSystemA {
public void methodA() {
//业务实现代码
}
}
public class SubSystemB {
public void methodB() {
//业务实现代码
}
}
public class SubSystemC {
public void methodC(){
//业务实现代码
}
}
外观类
public class Facade {
private SubSystemA obj1 = new SubSystemA();
private SubSystemB obj2 = new SubSystemB();
private SubSystemC obj3 = new SubSystemc();
public void method() {
obj1.methodA();
obj2.methodB();
obj3.methodC();
}
}
客户类
public class Client {
public static void main(String args[]){
Facade facade = new Facade();
facade.method();
}
}
外观模式与单例模式联用
优点:
- 对客户端屏蔽了子系统组件,减少了客户端所需处理的对象数目,并使得子系统使用起来更加容易
- 现了子系统与客户端之间的松耦合关系,这使得子系统的变化不会影响到调用它的客户端,只需要调整外观类即可
- 子系统的内部变化不会影响到外观对象,一个子系统的修改对其他子系统也没有任何影响
缺点:
- 不能很好地限制客户端直接使用子系统类,如果对客户端访问子系统类做太多的限制则减少了可变性和灵活性
- 如果设计不当,增加新的子系统可能需要修改外观类的源代码,违背了开闭原则
适用环境:
- 要为访问一系列复杂的子系统提供一个简单入口
- 客户端程序与多个子系统之间存在很大的依赖性
- 在层次化结构中,可以使用外观模式的定义系统中每一层的入口,层与层之间不直接产生联系,而是通过外观类建立联系,降低层之间的耦合度
代理模式
代理模式结构:
- Subject(抽象主题角色)
- Proxy(代理主题角色)
- RealSubject(真实主题角色)
抽象主题类
public abstract class Subject {
public abstract void request();
}
真实主题类
public class RealSubject extends Subject{
public void request() {
//业务方法具体实现代码
}
}
代理类
public class Proxy extends Subject {
private RealSubject realSubject = new RealSubject();
//维持一个对真实主题 对象的引用
public void preRequest() {
……
}
public void request(){
preRequest();
realSubject.request();
//调用真实主题对象的方法
postRequest();
}
public void postRequest() {
……
}
}
常见的代理模式:
- 远程代理(Remote Proxy):为一个位于不同的地址空间的对象提供一个本地的代理对象,这个不同的地址空间可以在同一台主机中,也可以在另一台主机中,远程代理又称为大使(Ambassador)
- 虚拟代理(Virtual Proxy):如果需要创建一个资源消耗较大的对象,先创建一个消耗相对较小的对象来表示,真实对象只在需要时才会被真正创建
- 保护代理(Protect Proxy):控制对一个对象的访问,可以给不同的用户提供不同级别的使用权限
- 缓冲代理(Cache Proxy):为某一个目标操作的结果提供临时的存储空间,以便多个客户端可以共享这些结果
- 智能引用代理(Smart Reference Proxy):当一个对象被引用时,提供一些额外的操作,例如将对象被调用的次数记录下来等
优点:
- 共有优点:
- 能够协调调用者和被调用者,在一定程度上降低了系统的耦合度
- 客户端可以针对抽象主题角色进行编程,增加和更换代理类无须修改源代码,符合开闭原则,系统具有较好的灵活性和可扩展性
- 不同类型优点:
- 远程代理:可以将一些消耗资源较多的对象和操作移至性能更好的计算机上,提高了系统的整体运行效率
- 虚拟代理:通过一个消耗资源较少的对象来代表一个消耗资源较多的对象,可以在一定程度上节省系统的运行开销
- 缓冲代理:为某一个操作的结果提供临时的缓存存储空间,以便在后续使用中能够共享这些结果,优化系统性能,缩短执行时间
- 保护代理:可以控制对一个对象的访问权限,为不同用户提供不同级别的使用权限
缺点:
- 由于在客户端和真实主题之间增加了代理对象,因此有些类型的代理模式可能会造成请求的处理速度变慢(例如保护代理)
- 实现代理模式需要额外的工作,而且有些代理模式的实现过程较为复杂(例如远程代理)
使用环境:
- 当客户端对象需要访问远程主机中的对象时可以使用远程代理
- 当需要用一个消耗资源较少的对象来代表一个消耗资源较多的对象,从而降低系统开销、缩短运行时间时可以使用虚拟代理
- 当需要为某一个被频繁访问的操作结果提供一个临时存储空间,以供多个客户端共享访问这些结果时可以使用缓冲代理
- 当需要控制对一个对象的访问,为不同用户提供不同级别的访问权限时可以使用保护代理
- 当需要为一个对象的访问(引用)提供一些额外的操作时可以使用智能引用代理
行为型设计模式(重点)
分类:
- 类行为型模式:使用继承关系在几个类之间分配行为,主要通过多态等方式来分配父类与子类的职责
- 对象行为型模式:使用对象的关联关系来分配行为,主要通过对象关联等方式来分配两个或多个类的职责
模式名称 | 定义 |
---|---|
职责链模式 | 避免将一个请求的发送者与接收者耦合在一起,让多个对象都有机会处理请求。将接受请求的对象连接成一条链,并且沿着这条链传递请求,直到有一个对象能够处理它为止 |
命令模式 | 将—个请求封装为一个对象,从而让你可以用不同的请求对客户进行参数化,对请求排队或者记录请求日志,以及支持可撤销的操作。 |
解释器模式 | 给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。 |
迭代器模式 | 提供—种方法顺序访问一个聚合对象中的各个元素,且不用暴露该对象的内部表示。 |
中介者模式 | 定义一个对象来封装一系列对象的交互。中介者模式使各对象之间不需要显式地相互引用,从而使其耦合松散,而且让你可以独立地改变它们之间的交互。 |
备忘录模式 | 在不破坏封装的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,这样可以在以后将对象恢复到原先保存的状态。 |
观察者模式 | 定义对象之间的一种—对多依赖关系,使得每当一个对象状态发生改变时,其相关依赖对象都得到通知并被自动更新。 |
状态模式 | 允许一个对象在其内部状态改变时改变它的行为。对象看起来似乎修改了它的类。 |
策略模式 | 定义一系列算法,将每一个算法封装起来,并让它们可以相互替换,策略模式让算法可以独立于使用它的客户变化。 |
模板方法模式 | 定义一个操作中算法的框架,而将一些步骤延迟到子类中。模板方法模式使得子类不改变一个算法的结构即可重定义该算法的某些特定步骤。 |
访问者模式 | 表示一个作用于某对象结构中的各个元素的操作。访问者模式让你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。 |
观察者模式
观察者模式结构:
- Subject(目标)
- ConcreteSubject(具体目标)
- Observer(观察者)
- ConcreteObserver(具体观察者)
抽象目标类
import java.util.*;
public abstract class Subject {
//定义一个观察者集合用于存储所有观察者对象
protected ArrayList observers<Observer> = newArrayList();
//注册方法,用于向观察者集合中增加一个观察者
public void attach(Observer observer){
observers.add(observer);
}
//注销方法,用于在观察者集合中删除一个观察者
public void detach(Observer observer){
observers.remove(observer);
}
//声明抽象通知方法
public abstract void notify();
}
具体目标类
public class ConcreteSubject extends Subject {
//实现通知方法
public void notify(){
//遍历观察者集合,调用每一个观察者的响应方法
for(Object obs:observers){
((Observer)obs).update();
}
}
}
抽象观察者
public interface Observer {
//声明响应方法
public void update();
}
具体观察者
public class ConcreteObserver implements Observer {
//实现响应方法
public void update() {
//具体响应代码
}
}
客户端代码
……
Subject subject = new ConcreteSubject();
Observer observer = new ConcreteObserver();subject.attach(observer);ll注册观察者
subject.notify();
……
优点:
- 可以实现表示层和数据逻辑层的分离
- 在观察目标和观察者之间建立一个抽象的耦合
- 支持广播通信,简化了一对多系统设计的难度
- 符合开闭原则,增加新的具体观察者无须修改原有系统代码,在具体观察者与观察目标之间不存在关联关系的情况下,增加新的观察目标也很方便
缺点:
- 将所有的观察者都通知到会花费很多时间
- 如果存在循环依赖时可能导致系统崩溃
- 没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而只是知道观察目标发生了变化
适用环境:
- 一个抽象模型有两个方面,其中一个方面依赖于另一个方面,将这两个方面封装在独立的对象中使它们可以各自独立地改变和复用
- 一个对象的改变将导致一个或多个其他对象发生改变,且并不知道具体有多少对象将发生改变,也不知道这些对象是谁
- 需要在系统中创建一个触发链
策略模式
策略模式结构
- Context(环境类)
- Strategy(抽象策略类)
- ConcreteStrategy(具体策略类)
抽象策略类
public abstract class Strategy {
public abstract void algorithm();
//声明抽象算法
}
具体策略类
public class ConcreteStrategyA extends Strategy {
//算法的具体实现
public void algorithm() {
//算法A
}
}
环境类
public class Context {
private Strategy strategy;//维持一个对抽象策略类的引用
//注入策略对象
public void setStrategy(Strategy strategy) {
this.strategy= strategy;
}
//调用策略类中的算法
public void algorithm() {
strategy.algorithm();
}
}
客户端
……
Context context = new Context();
Strategy strategy;
strategy = new ConcreteStrategyA();
//可在运行时指定类型,通过配置文件和反射机制实现
context.setStrategy(strategy);
context.algorithm();
……
优点:
- 提供了对开闭原则的完美支持,用户可以在不修改原有系统的基础上选择算法或行为,也可以灵活地增加新的算法或行为
- 提供了管理相关的算法族的办法
- 提供了一种可以替换继承关系的办法可以避免多重条件选择语句
- 提供了一种算法的复用机制,不同的环境类可以方便地复用策略类
缺点:
- 客户端必须知道所有的策略类,并自行决定使用哪一个策略类
- 将造成系统产生很多具体策略类
- 无法同时在客户端使用多个策略类
适用环境:
- 一个系统需要动态地在几种算法中选择一种
- 避免使用难以维护的多重条件选择语句
- 不希望客户端知道复杂的、与算法相关的数据结构,提高算法的保密性与安全性