资讯专栏INFORMATION COLUMN

理解设计模式

saucxs / 2006人阅读

摘要:适配器模式将一个类的接口转换成客户希望的另外一个接口。代理模式为其他对象提供一种代理以控制对这个对象的访问。如果用来解决排序问题不符合开闭原则,添加策略需要修改代码用策略模式将策略抽象成接口,不同的策略实现该接口。

简单工厂、工厂方法、抽象工厂 简单工厂

通过定义多个factory.produceXXX()方法,或者通过向factory.produce(type)传递type参数来生成产品。
添加新产品需要添加produeXXX()方法或个性produce(type)来支持生成新的产品。

工厂方法

一个Factory接口,接口中有一个produce()方法用于生成产品,具体的XXXFactory实现Factory接口并实现自己的produce()方法。
添加新产品时新增一个XXXFactory类,实现Factory接口并实现produce()方法。

抽象工厂

一个Factory接口,接口中有多个produce()方法(produceA, produceB)每个方法对应生成一个类产品。
XXXFactory实现Factory接口,并实现其中的produce*()方法。
抽象工厂与工厂方法的区别,可以理解为抽象工厂用于生产多个产品类,工厂方法只生产一个产品类。

单例模式

保证一个类仅有一个实例,并提供一个静态的getInstance()方法获取这个实例。

懒汉式,不加锁,线程不安全
public class Singleton {  
    private static Singleton instance;  
    private Singleton (){}  
  
    public static Singleton getInstance() {  
        if (instance == null) {  
            instance = new Singleton();  
        }  
        return instance;  
    }  
}
懒汉式,加锁,线程安全
public class Singleton {  
    private static Singleton instance;  
    private Singleton (){}  
  
    public static synchronized Singleton getInstance() {  
        if (instance == null) {  
            instance = new Singleton();  
        }  
        return instance;  
    }  
}
饿汉式,线程安全

public class Singleton {

private static Singleton instance = new Singleton();  
private Singleton (){}  
public static Singleton getInstance() {  
    return instance;  
}  

}

懒汉式,双重检查,线程安全
public class Singleton {  
    private volatile static Singleton singleton;  
    private Singleton (){}  
    public static Singleton getSingleton() {  
        if (singleton == null) {  
            synchronized (Singleton.class) {  
                if (singleton == null) {  
                    singleton = new Singleton();  
                }  
            }  
        }  
        return singleton;  
    }  
}
懒汉式,静态内部类
public class Singleton {  
    private static class SingletonHolder {  
        private static final Singleton INSTANCE = new Singleton();  
    }  
    private Singleton (){}  
    public static final Singleton getInstance() {  
        return SingletonHolder.INSTANCE;  
    }  
}
通过枚举来实现单例
public enum Singleton {  
    INSTANCE;  
    public void whateverMethod() {  
    }  
}

自由序列化,线程安全,保证单例。

建造者模式

独立的Builder类,通过build方法来生成实例。在build之前用add、set等一系列方法为将要生成的类设置属性值。可以参考protobuf的对象创建方法。

原型模式

复制现有实例生成新的实例。复制方式有深克隆与浅克隆之分,具体看clone()方法的实现。

适配器模式

将一个类的接口转换成客户希望的另外一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
例:AudioPlayer.play()能能播放MP3,现在要它能播放其他格式(例如aac),另一个AACPlayer可以播放aac格式。实现方法:
1,创建一个MediaAdapter类,其中实例化一个AACPlayer用来播放aac格式
2,MediaAdapter实现play方法来调用AACPlayer的play方法,以此来实现适配
3,当用AudioPlayer播放aac格式时,调用MediaAdapter.play来间接调用AACPlayer的play方法
4,要用AudioPlayer播放所有其他格式,都在MediaAdapter中做适配。

桥模式

将抽象和实现放在两个不同的类层次中,使它们可以独立地变化。——《Head First 设计模式》
如何理解:

Bridge.java

public interface Bridge {
   public void func(int radius, int x, int y);
}

TheAbastractClass.java

public abstract class TheAbstractClass {
   protected Bridge bridge;
   protected TheAbstractClass(Bridge bridge){
      this.bridge = bridge;
   }
   public abstract void func();
}

BridgeOne.java

public class BridgeOne  implements Bridge {
   @Override
   public void func() {
      System.out.println("BridgeOne.func called!");
   }
}

BridgeTwo.java

public class BridgeTwo  implements Bridge {
   @Override
   public void func() {
      System.out.println("BridgeTwo.func called!");
   }
}

TheSpecificClass.java

public class TheSpecificClass extends TheAbastractClass{
    TheSpecificClass(Bridge bridge){
        super(bridge);
    }
    @override
    public void func(){
        this.bridge.func();
    }
}

一般来说,直接继承TheAbstractClass来实现func方法。在桥模式下,继承TheAbstractClass之后,func方法的具体实现则在通过this.bridge实现的。
桥模式和适配器模式的区别可以看这篇文章

过滤器模式

类似函数式编程中的filter

组合模式

对象中包含对象,用实现树型结构,对单个对象和组合对象的使用具有一致性。

装饰模式

用于动态地给一个对象添加一些额外的职责。
例:
Origin.java

public interface Origin{
   void func();
}

OriginImpl.java

public class OriginImpl implements Origin{
    public void func() {
        //do something
    }
}

AbstactDecorator.java

public abstract class AbstractDecorator implements Origin{
    protect Origin origin;
    public AbstractDecorator(Origin origin){
        this.origin = origin;
    }
    
    public void func(){
        this.origin.func();
    }
}

SpecificDecorator.java

public class SpecificeDecorator extends AbstractDecorator{
    public SpecificDecorator(Origin origin){
        super(origin);
    }    
    
    @Override
    public void func(){
        this.origin.func();
        //do something else
    }
}  

main

public static int main(){
    Origin origin = new SpecificeDecorator(new OriginImpl());
    origin.func();    
}

通过装饰器SpecificDecorator,可以在原来的OriginImpl.func()的基础上do something else

外观模式

用于隐藏系统的复杂性,提供一致的操作界面。
例如:
1,ClassA, ClassB, ClassC三个类分别有methodA, methodB, methodC三个方法
2,创建Facade类,关联ClassA, ClassB, ClassC,提供methodA, methodB, mehtodC
3,通过Facade就可以调用mehtodA, methodB, methodC了

享元模式

就是共享对象,将原来创建的对象保存起来,下次需要使用的时候优先使用已经创建过的相同对象。

代理模式

为其他对象提供一种代理以控制对这个对象的访问。

Origin.java

public interface Origin{
   void func();
}

OriginImpl.java

public class OriginImpl implements Origin{
    public void func() {
        //do something
    }
}

Proxy.java

public class Proxy implements Origin{
    private Origin origin;
    
    public void func(){
        if(origin == null){
            origin = new OriginImpl();
        }        
        this.origin.func();
    }    
}

Proxy为代理类,通过Proxy来间接的调用OriginImpl.func()

责任链模式

将请求的发送者和请求的处理者解耦。
当有多种类型(type)的Request,每个Request有一个指定的handler,通过把所有的handler连接起来形成一条链handlerLink。请求可以不用发送到指定的handler,直接发送到handlerLink,链中的每个handler判断type是不是自己的要处理的,如果不是则传递到下一个handler

命令模式

对命令请求者(Invoker)和命令实现者(Receiver)的解耦,方便对命令进行各种控制。

ICommand: 命令接口,有execute方法
Command: 命令实现类
Invoker: 请求发送者
Receiver: 请求接收者,负责执行实际的业务逻辑

不用命令模式:
想要调用Receiver的doSomething()方法

Receiver r = new Receiver();
r.doSomething()

命令模式:

Command.java

public Interface ICommand{
    void execute();
}

public class Command implements ICommand{
    private Receiver receiver;
    
    public Command(Receiver receiver){
        this.receiver = receiver;
    }
    
    public void execute(){
        receiver.doSomething();
    }
}

Invoker.java

public class Invoker{
    ICommand cmd;
    
    
    public void setCommand(ICommand cmd){
        this.cmd = cmd;
    }
    
    public void submit(){
        this.cmd.execute();
    }
}

main

Invoker invoker = new Invoker();
Receiver receiver = new Receiver();
ICommand cmd = new Command(receiver);
invoker.setCommand(cmd);
invoder.submit();

命令模式实现Invoker与Receiver的隔离,Command与Receiver耦合。发送者只接收命令。当要扩展新的功能的时候,创建新的ICommand的实现类交给Invoker即可。

解释器模式

个人理解就是,解释器的作用就是把人能看明白的东西解释一下,让机器也看明白。比如,python代码是给人看的,python解释器就是把代码解释成机器能执行的指令;计算表达式(1+2+3*4/7)是人能看懂的,计算器的作用就是把计算表达式解释成能执行的一系列的简单运算。

代码和计算表达式这样的问题有几个重要的特征:

用什么元素来书写,在计算表达式中就是数字(0-9)和运算符(+-*/),在代码中就是字母、数字和符号

怎么书写(文法),在计算表达式中运算符和数字必须交替出现,在代码中就是各种语言和各自的语法规则。

所以,有规则可循的问题都可以用解释器模式来解决。但是将问题的规则提炼出来是很麻烦的,而且规则很复杂的时候更加难以提炼,成本很高。因此,只有当一类问题发生的频率足够高的情况下才适合用解释器模式。

迭代器模式

为不同的数据结构提供统一的顺序遍历的操作。

中介者模式

假设有ABCD四个对象,如果两两交互的话每个对象要维护三条链路。
加入中介,每个对象维护一条链路。

备忘录模式

保存历史状态,以便可以恢复到原先的状态。

观察者模式

使用场景:一个对象A的改变要通知到一个或多个其他对象。

加入观察者可以让A不必关心具体要通知改变的其他对象,由观察者来发通知。

状态模式

类的行为是基于它的状态改变的,允许对象在内部状态发生改变时改变它的行为,对象看起来好像修改了它的类。如何理解这句话?

类(TheClass), 状态(State), 行为(action)

代码:

public class TheClass{
    private State state;
    
    public void action(){
        //TODO depends on state
    }
}

TODO这块可以用switch case或者if else来实现,State一般是一个int

typedef int State;//状态一般是个整型

public class TheClass{
    private State state;
    
    public void setState(State state){
        this.state = state;
    }
    
    public void action(){
        switch (state) {
            case stateA:
                //do A;
            case stateB:
                //do B;
        }
    }
}

不符合开闭原则,因为如果要增加一种状态,则需要修改代码,添加case

用状态模式,State是一个interface, 拥有一个action方法

public interface State{
    void action();
}

public Class StateA implements State{
    public void action(){
        //do A
    }
}

public Class StateB implements State{
    public void action(){
        //do B
    }
}

public class TheClass{
    private State state;
    
    public void setState(State state){
        this.state = state;
    }
    
    public void action(){
        this.state.action()
    }
}

如果要添加一个C状态,只需要新建一个StateC实现State接口即可。

空对象模式

对象有方法,NULL什么都没有。空对象目的在于提供默认的方法。这样我们空对象执行相关操作时也不会报错。

策略模式

从输入获取输出可以有多种策略,例如排序问题就有插入、冒泡、快速、归并等不同和策略,都可以得到相同的结果。各种策略分别适用于特定的输入,例如,对于本身有序数组来说,冒泡策略只需要遍历一遍就可以。

如果用if else来解决排序问题:

sort(List list, Strategy strategy){
    switch (strategy) {
        case insert:
            //do insert sort
        case bubble:
            //do bubble sort
        ...
    }
}

不符合开闭原则,添加策略需要修改代码

用策略模式:

sort(List list, Strategy strategy){
    strategy.sort(list)
}

将策略抽象成接口,不同的策略实现该接口。

模板模式

一个操作operate,有多个step。我们把operate定义为固定的模板,各个step则可以重写。

public abstract class Template{
    void step1();
    void step2();
    void step3();
    
    void final operate(){
    
        step1();
        
        step2();
        
        step3();
    }
}

public class TheClass extends Template{
    @override
    void step1(){
        //do step1
    }
    
    void step2(){
        //do step2        
    }
    
    void step3(){
        //do step3
    }
}
访问者模式

被访问者接口(Acceptor): 有accept(visitor)方法,接收visitor的访问

被访元素(AcceptorItem): 实现了Acceptor接口,通常不会直接暴露给Visitor。

访问者(Visitor): 有visit(acceptor)方法,访问acceptor元素

被访问对象(Containor): 直接暴露给Visitor出来的被访问对象,相当于一个Containor,以一定的结构组织了各个AcceptorItem。

Acceptor.java

public interface Acceptor{
    void acceptor(Visitor visitor);
}

Visitor.java

public interface Visitor{
    void visit(AcceptorOne acceptor);
    void visit(AcceptorTwo acceptor);
}

AcceptorOne.java

public class AcceptorOne extends{
    public void accept(Visitor visitor){
        visitor.visit(this);
    }
}

AcceptorTwo.java

public class AcceptorTwo extends{
    public void accept(Visitor visitor){
        visitor.visit(this);
    }
}

Containor.java

public class Containor implements Acceptor{
    
    private Collection acceptors;
    
    public void accept(Visitor visitor){
        Iteratro it = acceptors.getIterator();
        while(it.hasNext()){
            visitor.visit(it.next());
        }
    }
}

访问者(Vistor)有多个visit方法,visit根据不同的Accepctor执行不同的逻辑。如果要扩展新的Acceptor,需要修改Visitor接口,这样不符合开闭原则;而扩展新的Visitor时,则只需要新增Visitor实现就可以了。因此,访问者模式适合对象结构稳定,而经常需要扩展对象操作的场景。

MVC模式

MVC模式是一种软件架构模式,应于交互式应用中。交互式应用就是界面+操作+数据后台。如果不使用MVC模式,界面的代码和对数据操作的代码杂在一起,不方便重用,也不容易维护。MVC模式分离了界面和数据,界面(View)通过控制器(Controller)提供的接口操作数据模型(Model),后台数据变更之后通过控制器通知到相关的界面。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/73303.html

相关文章

  • [ 学习路线 ] 学完这些去阿里!GOGOGO

    摘要:以下知识点是前辈师兄总结基础语义化标签引进了一些新的标签,特别注意等,注意的标题结构理解浏览器解析的过程,理解的树形结构,及相应理解标签在各个浏览器上的默认样式代理样式,理解中的重置样式表的概念理解等功能性标签理解标签,理解文件提交过程推荐 以下知识点是前辈师兄总结 1、HTML/HTML5基础: 1.0、语义化H5标签1.1、H5引进了一些新的标签,特别注意article...

    zhaochunqi 评论0 收藏0
  • [ 学习路线 ] 学完这些去阿里!GOGOGO

    摘要:以下知识点是前辈师兄总结基础语义化标签引进了一些新的标签,特别注意等,注意的标题结构理解浏览器解析的过程,理解的树形结构,及相应理解标签在各个浏览器上的默认样式代理样式,理解中的重置样式表的概念理解等功能性标签理解标签,理解文件提交过程推荐 以下知识点是前辈师兄总结 1、HTML/HTML5基础: 1.0、语义化H5标签1.1、H5引进了一些新的标签,特别注意article...

    learn_shifeng 评论0 收藏0
  • 什么是代码架构(我对设计模式理解

    摘要:更好的理解设计模式我觉得对于设计模式的理解是把设计模式归并到架构的一部分,是架构的子集,重命名为代码架构,这样好理解很多。 设计模式,这是我听过最糟糕的翻译,这个名字对于程序员来说有点高高在上,难以理解,尤其是php,python,nodejs这些脚本语言的开发人员可能因为这个名字就忽视了设计模式的重要性。当然,除了名字以外,从更深层次,更具体来说,我觉得有三个原因: 不用设计模式也...

    mayaohua 评论0 收藏0
  • 什么是代码架构(我对设计模式理解

    摘要:更好的理解设计模式我觉得对于设计模式的理解是把设计模式归并到架构的一部分,是架构的子集,重命名为代码架构,这样好理解很多。 设计模式,这是我听过最糟糕的翻译,这个名字对于程序员来说有点高高在上,难以理解,尤其是php,python,nodejs这些脚本语言的开发人员可能因为这个名字就忽视了设计模式的重要性。当然,除了名字以外,从更深层次,更具体来说,我觉得有三个原因: 不用设计模式也...

    zone 评论0 收藏0
  • 一名【合格】前端工程师的自检清单

    摘要:在他的重学前端课程中提到到现在为止,前端工程师已经成为研发体系中的重要岗位之一。大部分前端工程师的知识,其实都是来自于实践和工作中零散的学习。一基础前端工程师吃饭的家伙,深度广度一样都不能差。 开篇 前端开发是一个非常特殊的行业,它的历史实际上不是很长,但是知识之繁杂,技术迭代速度之快是其他技术所不能比拟的。 winter在他的《重学前端》课程中提到: 到现在为止,前端工程师已经成为研...

    罗志环 评论0 收藏0
  • 一名【合格】前端工程师的自检清单

    摘要:在他的重学前端课程中提到到现在为止,前端工程师已经成为研发体系中的重要岗位之一。大部分前端工程师的知识,其实都是来自于实践和工作中零散的学习。一基础前端工程师吃饭的家伙,深度广度一样都不能差。开篇 前端开发是一个非常特殊的行业,它的历史实际上不是很长,但是知识之繁杂,技术迭代速度之快是其他技术所不能比拟的。 winter在他的《重学前端》课程中提到: 到现在为止,前端工程师已经成为研发体系...

    isaced 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<