设计模式-大话设计模式学习笔记

设计模式及使用的特性

UML示例

在这里插入图片描述

简单工厂模式(SimpleFactory)

用途:实例化对象,使用单独的类来实现创造实例,静态方法根据参数生产不同的实例。
使用特性:继承、多态。

策略模式(Strategy)

用途:它定义了算法家族,分别封装起来,让它们之间可以相互替换,此模式让算法的变化,不会影响到算法的用户,从而减少各种算法与使用算法之间的耦合(DPE),简化了单元测试(策略模式比简单工厂模式多了一个Context上下文类,该类会维护一个对父类型的引用,在该类初始化时会根据传入的参数来初始化对应的策略对象,在该类中实现策略对象的方法,从而将算法的配置从使用者移除)。
使用特性 :继承,多态,接口
与简单工厂模式的比较:对应使用者来说,简单工厂模式需要使用两个类(Factory和父类),而策略模式只需要一个Context类,Context通过参数来生成并对应对应的类(通常为某种算法)。
使用场景:只要需要在不同的时间应用不同的业务规则,就可以考虑使用策略模式处理这种变化的可行性。

单一职责原则(SPR)

定义:就一个类而言,应该仅有一个引起它变化的原因。
用途:降低耦合,分离UI和逻辑。

开放-封闭原则

定义:软件实体(类、模块、函数等等)应该可以扩展,但是不可以修改。
使用特性:对应扩展开放,对于修改封闭。
用途:在设计时,时刻考虑让类足够好,新的需求通过增加类来实现,通过构造抽象来隔绝修改中的变化。

依赖倒转原则

定义:高层模块不应该依赖底层模块,两个都应该依赖抽象。抽象不应该依赖细节,细节应该依赖于抽象(针对接口编程,不要对实现编程,程序中所有的依赖关系都终止于抽象类或者接口)。

里氏转换原则

定义:子类型必须能够替换掉他们的父类型。
用途:由于里氏转换原则,才使得开放-封闭原则成为可能(正由于子类型的可替换性才使得使用父类型的模块在无需修改的情况下可以扩展)。

装饰模式(Decorate)

定义:动态的给对象添加一些额外的职责,就功能来说,装饰模式比生成子类更加灵活。
在这里插入图片描述用途:装饰器的价值在于装饰,他并不影响被装饰类本身的核心功能。在一个继承的体系中,子类通常是互斥的。比如一辆车,品牌只能要么是奥迪、要么是宝马,不可能同时属于奥迪和宝马,而品牌也是一辆车本身的重要属性特征。但当你想要给汽车喷漆,换坐垫,或者更换音响时,这些功能是互相可能兼容的,并且他们的存在不会影响车的核心属性:那就是他是一辆什么车。这时你就可以定义一个装饰器:喷了漆的车。不管他装饰的车是宝马还是奥迪,他的喷漆效果都可以实现。实际使用中可以用来装饰日志输出,从而丰富Log的功能。
代码实现

//Java版本

//基础接口
public interface Component {
    
    public void biu();
}
//具体实现类
public class ConcretComponent implements Component {

    public void biu() {
        
        System.out.println("biubiubiu");
    }
}
//装饰类
public class Decorator implements Component {

    public Component component;
    
    public Decorator(Component component) {
        
        this.component = component;
    }
    
    public void biu() {
        
        this.component.biu();
    }
}
//具体装饰类
public class ConcreteDecorator extends Decorator {

    public ConcreteDecorator(Component component) {

        super(component);
    }

    public void biu() {
        
        System.out.println("ready?go!");
        this.component.biu();
    }
}

代理模式(Proxy)

定义:为其他对象提供一种代理已以控制对这个对象的访问。
UML图
在这里插入图片描述
在上面类图中,代理模式所涉及的角色有三个:

抽象主题角色(Subject):抽象主题类声明了真实主题类和代理类的公共方法,它可以是接口、抽象类或具体类,这样一来在使用真实主题的任何地方都可以使用代理主题。

代理主题角色(Proxy):代理主题角色内部含有对真实主题的引用,从而可以操作真实主题对象;代理主题角色负责在需要的时候创建真实主题对象;代理角色通常在将客户端调用传递到真实主题之前或之后,都要执行一些其他的操作,而不是单纯地将调用传递给真实主题对象。例如这里的PreRequestt和PostRequest方法就是代理主题角色所执行的其他操作。

真实主题角色(RealSubject):定义了代理角色所代表的真是对象。
调用时,Client通过Proxy间接的访问RealSubject
代码实现

//C#版本
//抽象类
abstract class Subject
{
    public abstract void Request();
}
//真实主题角色
class RealSubject : Subject
{
    public override void Request()
    {
        //业务方法具体实现代码
    }
}
//代理主题角色
class Proxy : Subject
{
    private RealSubject realSubject = new RealSubject(); //维持一个对真实主题对象的引用
     
    public void PreRequest() 
    {
        …...
    }
     
    public override void Request() 
    {
        PreRequest();
        realSubject.Request(); //调用真实主题对象的方法
         PostRequest();
    }
     
    public void PostRequest() 
    {
        ……
    }
}

用途: 代理模式的结构比较简单,其核心是代理类,为了让客户端能够一致性地对待真实对象和代理对象,在代理模式中引入了抽象层。在实际开发过程中,代理类的实现比上述代码要复杂很多,代理模式根据其目的和实现方式不同可分为很多种类,其中常用的几种代理模式简要说明如下:

   (1) 远程代理(Remote Proxy):为一个位于不同的地址空间的对象提供一个本地的代理对象,这个不同的地址空间可以是在同一台主机中,也可是在另一台主机中,远程代理又称为大使(Ambassador)。

   (2) 虚拟代理(Virtual Proxy):如果需要创建一个资源消耗较大的对象,先创建一个消耗相对较小的对象来表示,真实对象只在需要时才会被真正创建。

   (3) 保护代理(Protect Proxy):控制对一个对象的访问,可以给不同的用户提供不同级别的使用权限。

   (4) 缓冲代理(Cache Proxy):为某一个目标操作的结果提供临时的存储空间,以便多个客户端可以共享这些结果。

   (5) 智能引用代理(Smart Reference Proxy):当一个对象被引用时,提供一些额外的操作,例如将对象被调用的次数记录下来等。

   在这些常用的代理模式中,有些代理类的设计非常复杂,例如远程代理类,它封装了底层网络通信和对远程对象的调用,其实现较为复杂。

工厂方法模式/工厂模式(FactoryMethod)

定义:定义一个用于创建对象的接口,让继承该接口的每个子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到了子类。
优点工厂方法模式之所以可以解决简单工厂的模式,是因为它的实现把具体产品的创建推迟到子类中,此时工厂类不再负责所有产品的创建,而只是给出具体工厂必须实现的接口,这样工厂方法模式就可以允许系统不修改工厂类逻辑的情况下来添加新产品,这样也就克服了简单工厂模式中缺点
UML图
在这里插入图片描述
抽象产品角色(Product):定义产品的接口
具体产品角色(ConcreteProduct) :实现接口Product的具体产品类
抽象工厂角色(Creator) :声明工厂方法(FactoryMethod),返回一个产品
真实的工厂(ConcreteCreator):实现FactoryMethod工厂方法,由客户调用,返回一个产品的实例

原型模式(Prototype)

定义:用原型实例指定创建对象的种类,并且通过拷贝这些对象原型创建新的对象。(原型模式其实就是从一个对象再创建另一个可定制的对象,而且不需要知道任何创建的细节。)
实现方法: 1、实现克隆操作,在 JAVA 继承 Cloneable,重写 clone(),在 .NET 中可以使用 Object 类(需要继承ICloneable接口)的 MemberwiseClone() 方法来实现对象的浅拷贝(通过对引用类型的类实现ICloneable接口的MemberwiseClone方法也能实现深拷贝,但是要注意深入的层次和循环引用的问题)或通过序列化的方式来实现深拷贝。 2、原型模式同样用于隔离类对象的使用者和具体类型(易变类)之间的耦合关系,它同样要求这些"易变类"拥有稳定的接口。

使用场景

 1.在需要一个类的大量对象的时候,使用原型模式是最佳选择,因为原型模式是在内存中对这个对象进行拷贝,要比直接new这个对象性能要好很多,在这种情况下,需要的对象越多,原型模式体现出的优点越明显。
 2.如果一个对象的初始化需要很多其他对象的数据准备或其他资源的繁琐计算,那么可以使用原型模式。
 3.当需要一个对象的大量公共信息,少量字段进行个性化设置的时候,也可以使用原型模式拷贝出现有对象的副本进行加工处理。

模板方法模式(TemplateMethod)

定义:定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。
作用:模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
UML图
在这里插入图片描述

迪米特法则/最少知识原则

定义:如果两个类不必彼此直接通信,那么这两个类就不应当发生直接的相互作用。如果其中一个需要调用另一个类的某一个方法的话,可以通过第三者转发这个调用。
迪米特法则首先强调的前提使在类的结构设计上,每一个类都应当尽量降低成员的访问权限。也就是说,一个类包装好自己的private状态,不需要让别的类知道的字段或行为就不要公开,需要公开的字段,通常用属性来体现(封装)。

外观模式(Facade)

定义 :为子系统的一组接口提供一个一致的界面,此模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。
UML图
在这里插入图片描述使用场景
1.在设计的初期阶段,应该有意识地将不同的两个层分离,比如经典的三层架构。
2.在开发阶段,子系统往往因为不断的重构演化而变地越来越复杂,增加外观 Facade 可以提供一个简单的接口,减少依赖。
3.在维护一个遗留的大型系统时,增加一个外观 Facade 类,让 新系统与Facade对象交互,Facade与遗留代码加护所有复杂的工作。

建造者模式(Builder)

定义 :将一个复杂的构建与其表示相分离,使得同样的构建过程可以创建不同的表示。

UML图
在这里插入图片描述作用:主要是用于创建一些复杂的对象,这些对下你个内部构建间的顺序通常是稳定的,但是对象内部的构建通常面临着复杂的变化。

观察者模式(Observer)

定义 :观察这模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象,这个主题对象在状态发生变化时,会通知所有观察者对象,让它们能够自动更新自己。
使用场景:当一个对象的改变需要同时改变其他对象而且它不知道具体有多少对象有待改变的时候。
一个抽象模型有两个方面,其中一个方面依赖另一个方面。
观察这模式所作的工作就是接触耦合,让耦合的双方都依赖于抽象,而不是依赖于具体,从使得各自的变化都不会影响另一边的变化。
UML图
在这里插入图片描述

抽象工厂模式(Abstract Factory)

定义 :提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们的具体的类。
UML图
在这里插入图片描述优点 :当一个产品族中的多个对象被设计成一起工作时,它能保证客户端始终只使用同一个产品族中的对象。

缺点 :扩展非常,要增加一个系列的某一产品,既要在抽象的 Creator 里加代码,又要在具体的里面加代码。

状态模式(State)

定义 :当一个对象的内在状态改变时允许改变其行为,这个对象看起来像是改变了其类。
使用场景 :状态模式主要解决的时当控制一个对象状态转换的条件表达式过于复杂时的情况。把状态的判定逻辑转移到表示不同状态的一系列类中,可以把复杂的判断逻辑简化。‘
UML图
在这里插入图片描述

适配器模式(Adapter)

定义:将一个类的接口转换成客户希望的另一个接口。Adapter模式 使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
UML图
在这里插入图片描述

备忘录模式(Memento)

定义:在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可以将该对象恢复到保存之前的状态。
使用场景 :备忘录模式适用于功能比较复杂,但是需要维护和记录属性历史的类,或者需要保存的属性只是众多属性中的一小部分时,Originator可以根据保存的Memento信息还原到前一状态。
UML图
在这里插入图片描述
优点: 1、给用户提供了一种可以恢复状态的机制,可以使用户能够比较方便地回到某个历史的状态。 2、实现了信息的封装,使得用户不需要关心状态的保存细节。
缺点:消耗资源。如果类的成员变量过多,势必会占用比较大的资源,而且每一次保存都会消耗一定的内存。

组合模式(Composite)

定义:将对象组合成树形结构以表示 ‘部分-整体’ 的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。
使用场景:当发现需求中是体现部分于整体层次的结构时,以及希望用户可以忽略组合对象与单个对象的不同,统一得使用组合结构中的所有对象时,就应该考虑组合模式。
优点: 1、高层模块调用简单。 2、节点自由增加。
缺点:在使用组合模式时,其叶子和树枝的声明都是实现类,而不是接口,违反了依赖倒置原则。
UML图
在这里插入图片描述

迭代器模式(Iterator)

定义:提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露该对象的内部表示。
使用场景:当需要访问一个聚集对象,而且不管这些对象是什么都需要遍历的时候,需要考虑用迭代器模式。为遍历不同的聚集结构提供如开始、下一个、是否结束、当前哪一项等统一的接口。(C#中的foreach提供了迭代器模式的实现)
UML图
在这里插入图片描述
.NET的迭代器实现:IEnumerator、IEnumerable接口

单例模式(Singleton)

定义:保证一个类仅有一个实例,并且提供一个访问它的全局访问点。
关键:构造函数是私有的,只有一个访问点且访问点是静态的。
注意:多线程开发时对访问点加锁。
优点
1、在内存里只有一个实例,减少了内存的开销,尤其是频繁的创建和销毁实例(比如管理学院首页页面缓存)。
2、避免对资源的多重占用(比如写文件操作)。

缺点:没有接口,不能继承,与单一职责原则冲突,一个类应该只关心内部逻辑,而不关心外面怎么样来实例化。
普通单利模式

        private static Singleton instance;
        private Singleton() { }
        public static Singleton GetInstance() {
            if (instance == null) {
                instance = new Singleton();
            }
            return instance;
        }
    }

线程安全的单利模式代码

        private static Singleton instance;
        private static readonly object syncRoot = new object();
        private Singleton() { }
        public static Singleton GetInstance() {
            if (instance == null) {
                lock (syncRoot) {
                    if (instance == null) {
                        instance = new Singleton();
                    }
                }
            }
            return instance;
        }
    }

静态初始化的单利模式

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

桥接模式(Bridge)

定义:将抽象部分与它的实现部分分离,使它们可以独立地变化。
合成/聚合复用原则:尽量使用合成/聚合,尽量不要使用类继承。

在这里插入图片描述
合成/聚合复用原则的优点:优先使用合成/聚合复用原则将有助于保持每个类被封装,并被集中在单个任务上,这样类和类继承层次会保持较小的规模,并且不太可能增长成为不可控制的庞然大物。
UML图
在这里插入图片描述使用场景:实现系统可能有多角度分类,每一种分类都有可能变化,那么就把这种多角度分离出来让它们独立变化,减少它们之间的耦合。

命令模式(Command)

定义:将一个请求封装为一个对象,从而可以使用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤销的操作。
UML图
在这里插入图片描述
优点
1、能够较容易的设计一个命令队列。
2、在需要的情况下,可以较容易的将命令记入日志。
3、允许接收请求的一方决定是否要否决请求。
4、可以容易的实现对请求的撤销和重做。
5、由于加入新的具体命令类不影响其他的类,依次增加新的具体命令类很容易。
6、把请求操作的对象与操作的执行对象分割。

职责链模式(Chain of Responsibility)

定义:使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这个对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。
UML图
在这里插入图片描述优点: 1、降低耦合度。它将请求的发送者和接收者解耦。 2、简化了对象。使得对象不需要知道链的结构。 3、增强给对象指派职责的灵活性。通过改变链内的成员或者调动它们的次序,允许动态地新增或者删除责任。 4、增加新的请求处理类很方便。

缺点: 1、不能保证请求一定被接收。 2、系统性能将受到一定影响,而且在进行代码调试时不太方便,可能会造成循环调用。 3、可能不容易观察运行时的特征,有碍于除错。

中介者模式(Mediator)

定义:用一个中介对象来封装一系列的对象交互。中介者使各对象不需要显式的相互引用,从而使耦合松散,而且可以独立的改变他们的交互。
UML图
在这里插入图片描述何时使用:多个类相互耦合,形成了网状结构。
优点: 1、降低了类的复杂度,将一对多转化成了一对一。 2、各个类之间的解耦。 3、符合迪米特原则。
缺点:中介者会变地庞大复杂而难以维护。

享元模式(Flyweight)

定义:运用共享技术有效的支持大量细粒度的对象。
UML图
在这里插入图片描述
应用场景: 1、系统中有大量对象。 2、这些对象消耗大量内存。 3、这些对象的状态大部分可以外部化。 4、这些对象可以按照内蕴状态分为很多组,当把外蕴对象从对象中剔除出来时,每一组对象都可以用一个对象来代替。 5、系统不依赖于这些对象身份,这些对象是不可分辨的。
特定:用 HashMap 存储这些对象。

优点:大大减少对象的创建,降低系统的内存,使效率提高。

缺点:提高了系统的复杂度,需要分离出外部状态和内部状态,而且外部状态具有固有化的性质,不应该随着内部状态的变化而变化,否则会造成系统的混乱。

解释器模式(interpreter)

定义:给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。
优点: 1、可扩展性比较好,灵活。 2、增加了新的解释表达式的方式。 3、易于实现简单文法。
缺点: 1、可利用场景比较少。 2、对于复杂的文法比较难维护。 3、解释器模式会引起类膨胀。 4、解释器模式采用递归调用方法。
使用场景: 1、可以将一个需要解释执行的语言中的句子表示为一个抽象语法树。 2、一些重复出现的问题可以用一种简单的语言来进行表达。 3、一个简单语法需要解释的场景。

访问者模式(Vistor)

定义:表示一个作用于某对象结构种的各元素的操作。它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。

优点: 1、符合单一职责原则。 2、优秀的扩展性。 3、灵活性。

缺点: 1、具体元素对访问者公布细节,违反了迪米特原则。 2、具体元素变更比较困难。 3、违反了依赖倒置原则,依赖了具体类,没有依赖抽象。

使用场景: 1、对象结构中对象对应的类很少改变,但经常需要在此对象结构上定义新的操作。 2、需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而需要避免让这些操作"污染"这些对象的类,也不希望在增加新操作时修改这些类。访问者可以对功能进行统一,可以做报表、UI、拦截器与过滤器。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章