设计模式学习笔记

493 阅读28分钟

类图

一个类一般包含三部分。

  • 类名 正体字说明可以被实例化 斜体字说明类为抽象类
  • 属性
  • 方法 + 表示 public - 表示private # 表示 protected

描述信息使用<<开头 使用>>结尾

关系

图中的关系主要有:

继承关系 Inheritance

表示类与类之间的继承关系,接口与接口之间的继承

表示方法:用一个空心箭头+ 实线,箭头指向父类

实现关系 Realization/Implementation

类对接口的实现关系

表示方法:空心箭头+虚线,箭头指向接口

关联关系 Association

类与类之间的链接,他使一个类知道另一个类的属性和方法

表示方法:实线+箭头,箭头指向被使用的类

聚合关系 Aggregation

是关联关系的一种,是强的关联关系。聚合关系时整体和个体的关系。关联关系的两个类处于同一个层次上,而聚合关系的两个类处于不同的层次,一个是整体,一个是部分

表示方法:空心菱形+实线 箭头指向整体

复合关系 Composition

关联关系的一种,比聚合关系强的关系,要求普通的聚合关系中代替整体的对象负责代表部分的对象的生命周期

表示方法:实心菱形+实线

依赖关系 dependency

表示类与类之间的连接,表示一个类依赖于另一个类的定义。例如如果A依赖B,则B体现为局部变量,方法的参数、或静态方法的调用

表示方法:虚线+箭头 箭头指向被依赖的一方,也就是指向局部变量

六大设计原则

单一职责原则

单一职责原则的英文名称是Single Responsibility Principle,简称 SRP

定义:应该有且仅有一个原因引起类的改变。

好处:

  • 类的复杂性降低,实现什么职责都有清晰明确的定义
  • 可读性提高
  • 可维护性提高
  • 变更引起的风险降低,一个接口修改只对相应的实现类有影响,对其他的接口无影响

单一职责主要是针对 接口 类 方法 使用,但实际应用时 ,建议接口(iOS中Protocol)一定要做到单一职责,类的设计尽量做到只有一个原因引起变化。

里氏替换原则

里氏替换原则的英文名是 Liskov Substitution Principle,简称LSP

他有两种定义:

  • 如果对每一个类型为S的对象o1,都要类型为T的对象o2,使得以T定义的所有程序P在所有的对象o1都替换成o2时,程序P的行为没有发生变化,那么类型S是类型T的子类型。
  • 所有引用基类的地方必须能透明地使用其子类的对象

里氏替换原则的四个含义

  • 子类必须完全实现父类的方法
  • 子类可以有自己的方法和属性
  • 子类覆盖或实现父类的方法时输入参数可以放大
  • 子类覆盖或实现父类的方法时输出结果可以被缩小

依赖倒置原则

英文名是 Dependence Inversion Principle,简写为DIP

定义包含三种含义:

  • 高层模块不应该依赖底层模块,两者都应该依赖其抽象
  • 抽象不应该依赖细节
  • 细节应该依赖抽象

接口隔离原则

什么是接口

  • 实例接口,在Java中声明一个类Person,使用 Person p = new Person()声明一个实例,这个实例遵循Person这个类,Person这个类就是实例 p 的接口。
  • 类接口 ,在Java中,使用 interface 关键字定义的接口

什么是隔离

  • 客户端不应该依赖他不需要的接口
  • 类间的依赖关系应该建立在最小的接口上

隔离原则要求“尽量使用多个专门的接口”,就是指给每个模块都应该是单一的接口,提供给几个模块就应该有几个接口,而不是建立一个庞大的臃肿的接口。

想一下美女的例子,容易理解。

迪米特法则

迪米特法则 (Law of Demeter,LoD),也称为最少知识原则(Least Knowledge Principle,LKP)

规则: 一个对象应该对其他对象有最少的了解。通俗的说,就是一个类应该对自己需要耦合或调用的类知道得最少,被耦合或调用的类内部如何复杂与自己无关,只需知道提供的public方法即可。

一个类只和自己的朋友类交流。(朋友类是指出现在成员变量、方法的输入输出参数中的类)

开闭原则

Open Closed Principle

定义:软件实体应该对扩展开放,对修改关闭,就是说一个软件实体应该通过扩展来实现变化,而不是通过已有的代码来实现变化。

什么是软件实体?

  • 项目或软件产品中按照一定的逻辑规则划分的模块
  • 抽象和类
  • 方法

其实,前五个原则都是开闭原则的具体形态。

23中设计模式

单例模式

定义

确保某个类有且只有一个单例,而且自行实例化并向整个系统提供这个实例。

通用代码

public class Singleton {
     private static final Singleton singleton = new Singleton();        
     //限制产生多个对象
     private Singleton(){
     }
     //通过该方法获得实例对象
     public static Singleton getSingleton(){
             return singleton;
     }  
     //类中其他方法,尽量是static
     public static void doSomething(){
     }
}

优点

  • 因为只有一个实例,可以减少内存的开支,还可以减少系统的性能开销,如读取配置、产生其他依赖对象时,可以通过在启动时候产生一个单例对象,然后用永久驻留内存的方式解决。
  • 可以避免对资源的多重占用
  • 可以在系统设置全局的访问点,优化和共享资源访问。如可设计一个单例,负责所有数据表的映射处理

缺点

  • 一般没有接口,很难扩展,若要扩展,除了修改代码基本没有第二种途径可以实现
  • 不利于测试
  • 与单一职责原则冲突

注意事项

在高并发情况下,注意线程同步问题。如下面这种实现方式,就会出现多个实例的情况

public class Singleton {
     private static Singleton singleton = null; 
     //限制产生多个对象
     private Singleton(){
          }  
     //通过该方法获得实例对象
     public static Singleton getSingleton(){
             if(singleton == null){
                    singleton = new Singleton();
             }
             return singleton;
     }
}

因为对象初始化也是需要时间的,如果一个线程执行到 singleton = new Singleton(),但还没有获得对象,另一个线程执行到singleton == null,那么判断条件为真,则内存中就出现两个对象。

解决方案:

在getSingleton方法前或方法内加 synchronized关键字,但最好的方式是使用第一种方式。

单例模式的扩展 多例模式

多例模式:

要求一个类只能产生具体个数的对象

实现(OC版):

@interface Emperor : NSObject

+ (instancetype)shared;

- (void)say;

@end


@implementation Emperor
{
    NSInteger _currentIndex;
}

static NSMutableArray *_emperors;

- (instancetype)initWithIndex:(NSInteger)index{
    self = [super init];
    if (self) {
        _currentIndex = index;
    }
    return self;
}

+ (instancetype)shared{
    
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        _emperors = [[NSMutableArray alloc]init];
        for (int i = 0; i < 3; i++) {
            Emperor *emperor = [[Emperor alloc]initWithIndex:i];
            [_emperors addObject:emperor];
        }
    });
    
    NSInteger index = arc4random() % 3;
    return [_emperors objectAtIndex:index];
}

- (void)say{
    NSLog(@"我是皇帝  %ld 号",_currentIndex);
}

@end

从而决定内存中可以有几个实例的存在,修改单例可能存在的性能问题,提高系统的响应速度。

工厂方法模式

定义

定义一个用于创建对象的接口,让子类决定实例化哪一个类,工厂方法使一个类的实例化延迟到其子类。

一般常用的通用源码

抽象产品类

public abstract class Product {    
     //产品类的公共方法
     public void method1(){
             //业务逻辑处理
     }  
     //抽象方法
     public abstract void method2();    
}

具体产品类,都继承自抽象产品类

public class ConcreteProduct1 extends Product {
     public void method2() {
             //业务逻辑处理
     }
}
public class ConcreteProduct2 extends Product {
     public void method2() {
             //业务逻辑处理
     }
}

抽象工厂类,负责定义产品对象的产生

public abstract class Creator {    
     /*
      * 创建一个产品对象,其输入参数类型可以自行设置
      * 通常为String、Enum、Class等,当然也可以为空
      */        
     public abstract <T extends Product> T createProduct(Class<T> c);
     }

具体工厂类

public class ConcreteCreator extends Creator {     
     public <T extends Product> T createProduct(Class<T> c){
             Product product=null;
             try {
                    product = (Product)Class.forName(c.getName()).newInstance();
             } catch (Exception e) {
                    //异常处理
             }
             return (T)product;         
     }
}

调用方法

public class Client {
     public static void main(String[] args) {
             Creator creator = new ConcreteCreator();
             Product product = creator.createProduct(ConcreteProduct1.class);
             /*
              * 继续业务处理
              */
     }
}

注:在OC 中 NSNumber的实现,是工厂模式

抽象工厂模式

定义

为创建一组相关或相互依赖的对象提供一个接口,而且无需指定他们的具体类,是工厂方法模式的升级版本,在有多个业务品种、业务分类时,通过该模式产生需要的对象是一种非常好的解决方案。

使用场景

一个对象族(或是一组没有任何关系的对象)都有相同的约束,则可以使用抽象工厂模式

举例说明

抽象产品类

public abstract class AbstractProductA {   
     //每个产品共有的方法
     public void shareMethod(){
     }
     //每个产品相同方法,不同实现
     public abstract void doSomething();
}

产品A1的实现类

public class ProductA1 extends AbstractProductA {
     public void doSomething() {
             System.out.println("产品A1的实现方法");
     }
}

产品A2的实现类

public class ProductA2 extends AbstractProductA {
     public void doSomething() {
             System.out.println("产品A2的实现方法");
     }
}

产品B与此类似。

抽象工厂类

public abstract class AbstractCreator {
     //创建A产品家族
     public abstract AbstractProductA createProductA(); 
     //创建B产品家族
     public abstract AbstractProductB createProductB();
}

产品等级1的实现类

public class Creator1 extends AbstractCreator {    
     //只生产产品等级为1的A产品
     public AbstractProductA createProductA() { 
             return new ProductA1();
     }
     //只生产产品等级为1的B产品
     public AbstractProductB createProductB() {
             return new ProductB1();
     }
}

产品等级2的实现类

public class Creator2 extends AbstractCreator {    
     //只生产产品等级为2的A产品
     public AbstractProductA createProductA() { 
             return new ProductA2();
     }
     //只生产产品等级为2的B产品
     public AbstractProductB createProductB() {
             return new ProductB2();
     }
     }

模板方法模式

定义

定义一个操作中的算法的框架,而将一些步骤延迟到子类中,使得子类可以不改变一个算的接口即可重定义该算法的某些特定步骤。

他的方法分为两类:

  • 基本方法 也叫基本操作,是由子类负责实现的方法,并且在模板方法中被调用
  • 模板方法 可以有一个或几个,一般是一个具体的方法,也就是一个框架,实现对基本方法的调度,完成固定的逻辑。

NOTE: 为防止恶意操作,一般模板方法都加上final关键字,不允许被覆写。

抽象模板类

public abstract class AbstractClass {
     //基本方法
     protected abstract void doSomething();
     //基本方法
     protected abstract void doAnything();
     //模板方法
     public void templateMethod(){
             /*
              * 调用基本方法,完成相关的逻辑
              */
             this.doAnything();
             this.doSomething();
     }
}

具体模板类

public class ConcreteClass1 extends AbstractClass {
     //实现基本方法
     protected void doAnything() {
             //业务逻辑处理
     }
     protected void doSomething() {
             //业务逻辑处理
     }
}
public class ConcreteClass2 extends AbstractClass {
     //实现基本方法
     protected void doAnything() {
             //业务逻辑处理
     }
     protected void doSomething() {
             //业务逻辑处理
     }
}

使用场景

public class Client {
     public static void main(String[] args) {
             AbstractClass class1 = new ConcreteClass1();
             AbstractClass class2 = new ConcreteClass2();               
             //调用模板方法
             class1.templateMethod();
             class2.templateMethod();
     }
}

使用场景

  • 多个子类有共有的方法,并且逻辑基本相同
  • 重要、复杂的算法,可以把核心算法设计为模板方法,周边的相关细节功能则由各个子类实现
  • 重构时,模板方法是一个经常使用的模式,可以把相同的代码抽取到父类中,然后通过钩子函数(外界条件改变,影响到模板方法的执行。在我们的抽象类中isAlarm的返回值就是影响了模板方法的执行结果,该方法就叫做钩子方法(Hook Method))约束其行为。

优点

  • 封装不变部分,扩展可变部分

  • 提取公共代码,便于维护

  • 行为由父类控制,子类实现

建造者模式

定义

将一个复杂对象的构建与他的表示分离,使得同样的构建过程可以创建不同的表示。

在建造者模式中,有四个角色:

  • Product 产品类

通常是实现了模板方法模式,也就是有模板方法和基本方法

  • Builder抽象建造者

规范产品的组件,一般是由子类实现

  • ConcreteBuilder 具体建造者

实现抽象类定义的所有方法,并且返回一个组建好的对象

  • Director导演类

负责安排已有模块的顺序,然后告诉Builder开始建造。

通用源码

产品类

public class Product {
     public void doSomething(){
             //独立业务处理
     }
}

抽象建造类

public abstract class Builder {    
     //设置产品的不同部分,以获得不同的产品
     public abstract void setPart();
     //建造产品
     public abstract Product buildProduct();
     }

具体建造类

public class ConcreteProduct extends Builder {
     private Product product = new Product();
     //设置产品零件
     public void setPart(){
             /*
              * 产品类内的逻辑处理
              */
     }  
     //组建一个产品
     public Product buildProduct() {
             return product;
     }
}

导演类

public class Director {
     private Builder builder = new ConcreteProduct();
     //构建不同的产品
     public Product getAProduct(){
             builder.setPart();
             /*
              * 设置不同的零件,产生不同的产品
              */
             return builder.buildProduct();
     }
}

使用场景

  • 相同的方法,不同的执行顺序,产生不同的事件结果时
  • 多个部件,可以装配到一个对象中,但是产生的运行结果又不相同时
  • 产品类非常复杂,或者产品类中的调用顺序不同产生了不同的效能
  • 在对象创建过程中会使用到系统中的一些其他对象,这些对象在产品对象的创建过程中不易得到时,可采用建造者模式封装该对象的创建过程。

总结

建造者模式和工厂模式有些相似,但建造者模式最主要的功能是基本方法的调用顺序的安排,也就是基本方法已经实现,顺序不同产生的对象也不同,而工厂方法重点是创建,顺序则不是它关心的。

原型模式(prototype pattern)

定义

用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象

原型模式的核心是一个clone方法,通过该方法进行对象的拷贝。

通用源码

public class PrototypeClass  implements Cloneable{
     //覆写父类Object方法
     @Override
     public PrototypeClass clone(){
             PrototypeClass prototypeClass = null;
             try {
                    prototypeClass = (PrototypeClass)super.clone();
             } catch (CloneNotSupportedException e) {
                    //异常处理
             }
             return prototypeClass;
     }
}

使用场景

  • 资源优化场景

类初始化需要消耗非常多的资源,这个资源包括数据、硬件资源等。

  • 性能和安全要求的场景

通过new产生一个对象需要非常繁琐的数据准备或访问权限,则可以使用原型模式。

  • 一个对象多个修改者的场景

一个对象需要提供给其他对象访问,而且各个调用者可能都需要修改其值时,可以考虑使用原型模式拷贝多个对象供调用者使用。

注意事项

  • 构造函数是不会执行的

clone方法的原理是从内存中以二进制流的方式进行拷贝,重新分配一个内存块

  • 浅拷贝和深拷贝
  • clone和final两个关键字是冲突的 要使用clone方法,类的成员变量上就不要增加final关键字

中介者模式

MVC框架中的C就是一个中介者

命令模式

定义

将一个请求封装成一个对象,从而让你使用不同的请求把客户端参数化,对请求排队或者记录请求日志,可以提供命令的撤销和恢复功能。

三个角色

  • Receive接收者角色

该角色就是干活的角色,命令传递到这里是应该被执行的

  • Command 命令角色

需要执行的所有命令都在这里声明

  • Invoker调用者角色

接收到命令,并执行命令。

通用代码

调用Receiver类

public abstract class Receiver {
     //抽象接收者,定义每个接收者都必须完成的业务
     public abstract void doSomething();
}

具体的Receiver类

public class ConcreteReciver1 extends Receiver{    
     //每个接收者都必须处理一定的业务逻辑
     public void doSomething(){
     }
}
public class ConcreteReciver2 extends Receiver{ 
     //每个接收者都必须处理一定的业务逻辑
     public void doSomething(){
     }
}

抽象的Command类

public abstract class Command {
     //每个命令类都必须有一个执行命令的方法
     public abstract void execute();
}

具体的Command类

public class ConcreteCommand1 extends Command {
     //对哪个Receiver类进行命令处理
     private Receiver receiver; 
     //构造函数传递接收者
     public ConcreteCommand1(Receiver _receiver){
             this.receiver = _receiver;
     }
     //必须实现一个命令
     public void execute() {
             //业务处理
             this.receiver.doSomething();
     }
}
public class ConcreteCommand2 extends Command {
     //哪个Receiver类进行命令处理
     private Receiver receiver;
     //构造函数传递接收者
     public ConcreteCommand2(Receiver _receiver){
             this.receiver = _receiver;
     }
     //必须实现一个命令
     public void execute() {
             //业务处理
             this.receiver.doSomething();
     }
}

可以根据实际应用扩展命令类,在命令类中,通过构造函数定义了该命令是针对哪一个接收者发出的。

调用者Invoker类

public class Invoker {
     private Command command;
     //受气包,接受命令
     public void setCommand(Command _command){
             this.command = _command;
     }
     //执行命令
     public void action(){
             this.command.execute();
     }
}

场景类

public class Client {
     public static void main(String[] args) {
             //首先声明调用者Invoker
             Invoker invoker = new Invoker();
             //定义接收者
             Receiver receiver = new ConcreteReciver1();
             //定义一个发送给接收者的命令
             Command command = new ConcreteCommand1(receiver);
             //把命令交给调用者去执行
             invoker.setCommand(command);
             invoker.action();
     }
}

使用场景

只要认为是命令的地方就可以采用命令模式

责任链模式

定义

使多个对象都有机会处理请求,从而避免了请求的发送者和接收者之间的耦合关系。将这些对象练成一条链,并沿着这条链传递该请求,知道有对象处理他为止。

通用源码

抽象处理者 主要实现三个职责:

  • 定义一个请求的处理方法 handleMessage,唯一对外开放的方法
  • 定义一个链的编排方法,设置下一个处理者
  • 定义具体的请求者必须实现的两个方法,定义自己能够处理的级别和具体的处理任务
public abstract class Handler {
     private Handler nextHandler;
     //每个处理者都必须对请求做出处理
     public final Response handleMessage(Request request){
             Response response = null;  
             //判断是否是自己的处理级别
             if(this.getHandlerLevel().equals(request.getRequestLevel())){
                    response = this.echo(request);
             }else{  //不属于自己的处理级别
                    //判断是否有下一个处理者
                    if(this.nextHandler != null){
                            response = this.nextHandler.handleMessage(request);
                    }else{
                            //没有适当的处理者,业务自行处理
                    }
             }
             return response;
     }
     //设置下一个处理者是谁
     public void setNext(Handler _handler){
             this.nextHandler = _handler;
     }
     //每个处理者都有一个处理级别
     protected abstract Level getHandlerLevel();
     //每个处理者都必须实现处理任务
     protected abstract Response echo(Request request);
}

具体处理者

主要涉及三个类

  • Level类负责定义请求和处理级别
  • Request类负责封装请求
  • Response负责封装链中返回的结果
public class ConcreteHandler1 extends Handler {
     //定义自己的处理逻辑
     protected Response echo(Request request) {
             //完成处理逻辑
             return null;
     }
     //设置自己的处理级别
     protected Level getHandlerLevel() {
             //设置自己的处理级别
             return null;
     }
}
public class ConcreteHandler2 extends Handler {
     //定义自己的处理逻辑
     protected Response echo(Request request) {
             //完成处理逻辑
             return null;
     }
     //设置自己的处理级别
     protected Level getHandlerLevel() {
             //设置自己的处理级别
             return null;
     }
}
public class ConcreteHandler3 extends Handler {
     //定义自己的处理逻辑
     protected Response echo(Request request) {
             //完成处理逻辑
             return null;
     }
     //设置自己的处理级别
     protected Level getHandlerLevel() {
             //设置自己的处理级别
             return null;
     }
}

有关框架代码

public class Level {
     //定义一个请求和处理等级
}
public class Request {
     //请求的等级
     public Level getRequestLevel(){
             return null;
     }
}
public class Response {
     //处理者返回的数据
}

场景类

public class Client {
     public static void main(String[] args) {
             //声明所有的处理节点
             Handler handler1 = new ConcreteHandler1();
             Handler handler2 = new ConcreteHandler2();
             Handler handler3 = new ConcreteHandler3();
             //设置链中的阶段顺序1-->2-->3
             handler1.setNext(handler2);
             handler2.setNext(handler3);
             //提交请求,返回结果
             Response response = handler1.handlerMessage(new Request());
     }
}

总结

责任链有很大的优点,可以将请求和处理分开,父类实现了请求的传递功能,子类实现了请求的处理,各个类只完成一个动作或逻辑,非常灵活。但也要注意,由于每次请求都需要从链头遍历到链尾,可能会存在性能问题,需要避免超长链问题。

可以考虑的优化方式:LFU(Least Frequently Used),按使用频率进行排序。

装饰模式

定义

动态地给一个对象添加一些额外的职责,就增加功能来说,装饰模式比生成子类更为灵活。

四个角色说明

  • Component抽象构件

一般是一个接口或抽象类,定义我们最核心的对象,也就是原始的对象

  • ConcreteComponent具体构件

最核心、最原始、最基本的接口或抽象类的实现,需要装饰的就是它

  • Decorator 装饰角色

一般是个抽象类,里面不一定有抽象的方法,属性里必然有一个private变量指向Component抽象构件

  • 具体装饰角色

具体的装饰类

通用源码

抽象构件

public abstract class Component {
     //抽象的方法
     public abstract void operate();
}

具体构件

public class ConcreteComponent extends Component {
     //具体实现
     @Override
     public void operate() {
             System.out.println("do Something");
     }
}

抽象装饰类

public abstract class Decorator extends Component {
     private Component component = null;        
     //通过构造函数传递被修饰者
     public Decorator(Component _component){
             this.component = _component;
     }
     //委托给被修饰者执行
     @Override
     public void operate() {
             this.component.operate();
     }
}

具体装饰类

public class ConcreteDecorator1 extends Decorator {
     //定义被修饰者
     public ConcreteDecorator1(Component _component){
             super(_component);
     }
     //定义自己的修饰方法
     private void method1(){
             System.out.println("method1 修饰");
     }
     //重写父类的Operation方法
     public void operate(){
             this.method1();
             super.operate();
     }
}
public class ConcreteDecorator2 extends Decorator {
     //定义被修饰者
     public ConcreteDecorator2(Component _component){
             super(_component);
     }
     //定义自己的修饰方法
     private void method2(){
             System.out.println("method2修饰");
     }
     //重写父类的Operation方法
     public void operate(){
             super.operate();
             this.method2();
     }
}

场景类

public class Client {
     public static void main(String[] args) {
             Component component = new ConcreteComponent();
             //第一次修饰
             component = new ConcreteDecorator1(component);
             //第二次修饰
             component = new ConcreteDecorator2(component);
             //修饰后运行
             component.operate();
     }
}

使用场景

装饰类和被装饰类可以独立发展,不会相互耦合,是继承关系的一个替代方案。

  • 需要扩展一个类的功能,或给一个类增加附加功能
  • 需要动态地给一个对象增加功能,这些功能可以在动态地撤销
  • 需要为一批的兄弟类进行改装或加装功能

策略模式

定义

定义一组算法,将每个算法都封装起来,并且使他们之间可以互换。

通用源码

抽象的策略角色

public interface Strategy {
     //策略模式的运算法则
     public void doSomething();
}

具体策略角色

public class ConcreteStrategy1 implements Strategy {
     public void doSomething() {
             System.out.println("具体策略1的运算法则");
     }
}

public class ConcreteStrategy2 implements Strategy {
     public void doSomething() {
             System.out.println("具体策略2的运算法则");
     }
}

封装角色

public class Context {
     //抽象策略
     private Strategy strategy = null;  
     //构造函数设置具体策略
     public Context(Strategy _strategy){
             this.strategy = _strategy;
     }
     //封装后的策略方法
     public void doAnythinig(){
             this.strategy.doSomething();
     }
}

高层模块

public class Client {
     public static void main(String[] args) {
             //声明一个具体的策略
             Strategy strategy = new ConcreteStrategy1();
             //声明上下文对象
             Context context = new Context(strategy);
             //执行封装后的方法
             context.doAnythinig();
     }
}

策略模式的重点是封装角色,借用了代理模式的思路,差别是策略模式的封装角色和被封装的策略类不是同一个接口,代理模式是同一个接口。

使用场景

  • 多个类只有在算法或行为上稍有不同
  • 算法需要自由切换的场景
  • 需要屏蔽算法规则的场景

适配器模式

定义

将一个类的接口变换成客户端所期待的另一种接口,从而使原本因接口不匹配而无法再一起工作的两个类能够在一起工作。

三个角色:

  • Target目标角色

该角色定义把其他类转换成何种接口,也就是我们期望的接口

  • Adaptee原角色

想把谁转换成目标角色,谁就是源角色,他是一个已经存在的,运行良好的类或对象,经过适配器角色的包装,成为一个崭新的角色

  • Adapter适配器角色

将源角色转换成目标角色

通用源码

目标角色

public interface Target {
     //目标角色有自己的方法
     public void request();
}

目标角色的实现

public class ConcreteTarget implements Target {
     public void request() {
             System.out.println("if you need any help,pls call me!");   }
}

源角色

public class Adaptee {
     //原有的业务逻辑
     public void doSomething(){
             System.out.println("I'm kind of busy,leave me alone,pls!");
     }
}

适配器

public class Adapter extends Adaptee implements Target {
     public void request() {
             super.doSomething();
     }
}

场景类

public class Client {
     public static void main(String[] args) {
             //原有的业务逻辑
             Target target = new ConcreteTarget();
             target.request();
             //现在增加了适配器角色后的业务逻辑
             Target target2 = new Adapter();
             target2.request();
     }
}

总结

该模式不是为了解决还处在开发阶段的问题,而是解决正在服役的项目问题,主要场景是在扩展应用中。

组合模式

定义

也叫合成模式,是将对象组合成树形结构以表示“部分-整体”的层次结构,使得用户对单个对象和组合对象的使用具有一致性。

三个角色:

  • Component 抽象构件角色

定义参加组合对象的共有方法和属性,可以定义一些默认的行为或属性

  • Leaf 叶子构件 叶子对象,其下再也没有的分支,也就是遍历的最小单位

  • Composite 树枝构件 树枝对象,他的作用是组合树枝节点和叶子节点行程一个树形结构

通用源码

抽象构件

public abstract class Component {
     //个体和整体都具有的共享
     public void doSomething(){
             //编写业务逻辑
     }
}

树枝构件

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 {
     /*
      * 可以覆写父类方法
      * public void doSomething(){
      * 
      * }
      */
}

场景类

public class Client {
     public static void main(String[] args) {
            //创建一个根节点
             Composite root = new Composite();
             root.doSomething();
             //创建一个树枝构件
             Composite branch = new Composite();
             //创建一个叶子节点
             Leaf leaf = new Leaf();
             //建立整体
             root.add(branch);
             branch.add(leaf);          
     }
     //通过递归遍历树
     public static void display(Composite root){
             for(Component c:root.getChildren()){
                  if(c instanceof Leaf){ //叶子节点
                          c.doSomething();
                  }else{ //树枝节点
                          display((Composite)c);
                  }
             }
     }
}

使用场景

  • 维护和展示部分-整体关系的场景,如树形菜单、文件和文件夹管理
  • 从一个整体能够独立出部分模块或功能的场景

门面模式

定义

也叫外观模式,要求一个子系统的外部语气内部的通信必须通过一个统一的对象进行。门面模式提供一个高层次的接口,使得子系统更易于使用。

两个角色

  • Facade 门面角色

客户端调用这个角色的方法,此角色知晓子系统的所有功能和责任

  • sybsystem 子系统角色

可以同时有一个或者多个子系统。没个子系统都不是一个单独的类,而是一个类的集合。子系统不知道门面的存在,对于子系统而言,门面仅仅是另一个客户端而已。

通用源码

子系统

public class ClassA {
     public void doSomethingA(){
             //业务逻辑
     }
}
public class ClassB {
     
     public void doSomethingB(){
             //业务逻辑
     }
}
public class ClassC {
     
     public void doSomethingC(){
             //业务逻辑
     }
}

门面对象

public class Facade {
     //被委托的对象
     private ClassA a = new ClassA();
     private ClassB b = new ClassB();
     private ClassC c = new ClassC();
     //提供给外部访问的方法
     public void methodA(){
             this.a.doSomethingA();
     }
     
     public void methodB(){
             this.b.doSomethingB();
     }
     
     public void methodC(){
             this.c.doSomethingC();
     }
}

使用场景

  • 为一个复杂的模块或子系统提供一个共外界访问的接口
  • 子系统相对独立,外界对紫铜的访问只要黑箱操作即可

总结

  • 一个子系统可以有多个门面
  • 门面不参与子系统内的业务逻辑

备忘录模式

定义

在不破坏封装性的前提下,补货一个对象的内部状态,并在这个对象之外保存这个状态,这样以后就可将该对象恢复到原先保存的状态。

三个角色

  • Originator 发起人角色

负责记录当前时刻的内部状态,定义哪些属于备份范围的状态,负责创建和恢复备忘录数据。

  • Memento 备忘录角色

负责存储Originator发起人对象的内部状态,在需要的时候提供发起人需要的内部状态

  • Caretaker 备忘录管理员角色

对备忘录进行管理、保存和提供备忘录。

通用源码

发起人角色

public class Originator {
     //内部状态
     private String 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 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;
     }
}

备忘录管理员角色

ublic 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();
             //创建一个备忘录
             caretaker.setMemento(originator.createMemento());
             //恢复一个备忘录
             originator.restoreMemento(caretaker.getMemento());
     }
}

扩展

  • clone方式的备忘录

利用原型模式,通过复制的方式产生一个对象内部状态,需要时候再还原。

访问者模式

定义

封装一些作用于某种数据结构的各元素的操作,他可以在不改变数据结构的前提下定义作用于这些元素的新的操作。

角色职责:

  • Visitor 抽象访问者

抽象类或者接口,声明访问者可以访问哪些元素

  • ConcreteVisitor 具体访问者

影响访问者访问到一个类后该怎么干,要做什么事情

  • Element 抽象元素

接口或者抽象类,声明接受那一个类访问者访问

  • ConcreteElement 具体元素

实现accept方法,通常是 visitor.visit(this)

  • ObjectStructure 结构对象

元素产生着,一般容纳在多个不同类、不同接口的容器。

通用源码

抽象元素

public abstract class Element {
     //定义业务逻辑
     public abstract void doSomething();
     //允许谁来访问
     public abstract void accept(IVisitor visitor);
}

具体元素

public class ConcreteElement1 extends Element{
     //完善业务逻辑
     public void doSomething(){
             //业务处理
     }
     //允许那个访问者访问
     public void accept(IVisitor visitor){
             visitor.visit(this);
     }
}
public class ConcreteElement2 extends Element{
     //完善业务逻辑
     public void doSomething(){
             //业务处理
     }
     //允许那个访问者访问
     public void accept(IVisitor visitor){
             visitor.visit(this);
     }
}

抽象访问者

public interface IVisitor {
     //可以访问哪些对象
     public void visit(ConcreteElement1 el1);
     public void visit(ConcreteElement2 el2);
}

具体访问者

public class Visitor implements IVisitor {
     //访问el1元素
     public void visit(ConcreteElement1 el1) {
             el1.doSomething();
     }
     //访问el2元素
     public void visit(ConcreteElement2 el2) {
             el2.doSomething();
     }
}

接口对象

public class ObjectStruture {
     //对象生成器,这里通过一个工厂方法模式模拟
     public static Element createElement(){
             Random rand = new Random();
             if(rand.nextInt(100) > 50){
                     return new ConcreteElement1();
             }else{
                     return new ConcreteElement2();
             }
     }
}

场景类

public class Client {
     public static void main(String[] args) {
             for(int i=0;i<10;i++){
                     //获得元素对象
                     Element el = ObjectStruture.createElement();
                     //接受访问者访问
                     el.accept(new Visitor());
             }          
     }
}

状态模式

定义

当一个对象内在状态改变时允许其改变行为,这个对象看起来像改变了其类。

三个角色

  • State 抽象状态角色

接口或抽象类,负责对象状态定义,并且封装环境角色以实现状态切换

  • ConcreteState 具体状态角色

每一个具体状态必须完成两个职责:本状态的行为管理以及趋向状态处理,通俗地说,就是本状态下要做的事和本状态如何过渡到其他状态

  • Context 环境角色

定义客户端需要的接口,负责具体状态的切换

通用源码

抽象环境角色

public abstract class State {
     //定义一个环境角色,提供子类访问
     protected Context context;
     //设置环境角色
     public void setContext(Context _context){
             this.context = _context;
     }
     //行为1
     public abstract void handle1();
     //行为2
     public abstract void handle2();
}

环境角色

public class ConcreteState1 extends State {
     @Override
     public void handle1() {
             //本状态下必须处理的逻辑
     }
     @Override
     public void handle2() {
             //设置当前状态为stat2
             super.context.setCurrentState(Context.STATE2);
             //过渡到state2状态,由Context实现
             super.context.handle2();
     }
}
public class ConcreteState2 extends State {
     @Override
     public void handle1() {          
             //设置当前状态为state1
             super.context.setCurrentState(Context.STATE1);
             //过渡到state1状态,由Context实现
             super.context.handle1();
     }
     @Override
     public void handle2() {
             //本状态下必须处理的逻辑
     }
}

具体环境角色

public class Context {
     //定义状态
     public final static State STATE1 = new ConcreteState1();
     public final static State STATE2 = new ConcreteState2();
     //当前状态
     private State CurrentState;
     //获得当前状态
     public State getCurrentState() {
             return CurrentState;
     }
     //设置当前状态
     public void setCurrentState(State currentState) {
             this.CurrentState = currentState;
             //切换状态
             this.CurrentState.setContext(this);
     }
     //行为委托
     public void handle1(){
             this.CurrentState.handle1();
     }
     public void handle2(){
             this.CurrentState.handle2();
     }
}

环境角色有两个不成文的约束:

  • 把状态对象声明为静态常量,有几个状态对象就声明几个静态常量。

  • 环境角色具有状态抽象角色定义的所有行为,具体执行使用委托方式。

场景类

public class Client {
     public static void main(String[] args) {
             //定义环境角色
             Context context = new Context();
             //初始化状态
             context.setCurrentState(new ConcreteState1());
             //行为执行
             context.handle1();
             context.handle2();
     }
}