1、图解设计模式 -----【日】结城浩 著
2、
基本介绍
对类来说的,即一个类应该只负责一项职责。如类A负责两个不同职责:职责1,职责2。当职责1需求变更而改变A是,可能造成职责2执行错误,所以需要将类A的粒度分解为A1,A2
注意事项和细节:
1、降低类的复杂度,一个类只负责一项职责。
2、提高类的可读性,可维护性
3、降低变更引起的风险
4、通常情况下,我们应当遵守单一职责原则,只有逻辑足等简单,才可以在代码级违反单一职责原则;只有类中方法数量足够少,可以在方法级别保持单一职责原则
基本介绍
客户端不应该依赖它不需要的接口,即一个类通过接口去依赖另一个类,所依赖的接口应该是最小的接口
实例:
使用接口隔离原则改进:
基本介绍
1)高层模块不应该依赖低层模块,二者都应该依赖其抽象
2)抽象不应该依赖细节,细节应该依赖抽象
3)依赖倒转(倒置)的中心思想是面向接口编程
4)依赖倒转原则是基于这样的设计理念:相对于细节的多变性,抽象的东西要稳定的多。以抽象为基础搭建的架构比以细节为基础的架构要稳定的多。在java中,抽象指的是接口或抽象类,细节就是具体的实现类
5)使用接口或抽象类的目的是制定好规范,而不涉及任何具体的操作,把展现细节的任务交给他们的实现类去完成
基本介绍
如果对每个类型为T1的对o1,都有类型为T2的对象o2,使得以T1定义的所有程序P在所有的对象o1都代换成o2时,程序P的行为没有发生变化,那么类型T2是类型T1的子类型。换句话说,所有引用基类的地方必须能透明地使用其子类的对象。
在使用继承时,遵循里氏替换原则,在子类中尽量不要重写父类的方法
里氏替换原则告诉我们,继承实际上让两个类耦合性增强了,在适当的情况下,原来的父类和子类都继承一个更通俗的基类,采用聚合
,组合
,依赖
等关系替代
基本介绍
1)开闭原则(Open Closed Principle)是编程中最基础、最重要的设计原则
2)一个软件实体如类,模块和函数应该对扩展开放(对提供方),对修改关闭(对使用方)。用抽象构建框架,用实现扩展细节。
3)当软件需要变化时,尽量通过扩展软件实体的行为来实现变化,而不是通过修改已有的代码来实现变化。
4)编程中遵循其它原则,以及使用设计模式的目的就是遵循开闭原则。
案例
方案1:
方案1优缺点:
1)优点是比较好理解,简单易操作。
2)缺点是违反了设计模式的ocp原则,即对扩展开放(提供方),对修改关闭(使用方)。
即当我们给类增加新功能的时候,尽量不修改代码,或者尽可能少修改代码.
3)比如我们这时要新增加一个图形种类三角形,我们需要修改的地方较多
方案2:
把创建Shape类做成抽象类,并提供一个抽象的draw方法,让子类去实现即可,这样我们有新的图形种类时,只需要让新的图形类继承Shape,并实现draw方法即可,使用方的代码就不需要修改->满足了开闭原则
基本介绍
1)一个对象应该对其他对象保持最少的了解
2)类与类关系越密切,耦合度越大
3)迪米特法则(Demeter Principle)又叫最少知道原则,即一个类对自己依赖的类知道的越少越好。也就是说,对于被依赖的类不管多么复杂,都尽量将逻辑封装在类的内部。对外除了提供的public 方法,不对外泄露任何信息
4)迪米特法则还有个更简单的定义:只与直接的朋友通信
5)直接的朋友:每个对象都会与其他对象有耦合关系,只要两个对象之间有耦合关系,我们就说这两个对象之间是朋友关系。耦合的方式很多,依赖,关联,组合,聚合等。其中,我们称出现成员变量,方法参数,方法返回值中的类为直接的朋友,而出现在局部变量中的类不是直接的朋友。也就是说,陌生的类最好不要以局部变量的形式出现在类的内部。
迪米特法则注意事项和细节
1)迪米特法则的核心是降低类之间的耦合
2)但是注意:由于每个类都减少了不必要的依赖,因此迪米特法则只是要求降低类间(对象间)耦合关系,并不是要求完全没有依赖关系
基本介绍
原则是尽量使用合成/聚合的方式,而不是使用继承
实例:
只要是在类中用到了对方,那么他们之间就存在依赖关系。如果没有对方,连编绎都通过不了。
public class PersonServiceBean{
private PersonDao personDao;//类
public void save(Person person){}
public IDCard getlDCard(Integer personid){}
public void modify(){
Department department=new Department();
}
}
public class PersonDao{}
public class IDCard{}
public class Person{}
public class DFpartment{}
泛化关系实际上就是继承关系,他是依赖关系的特例
public abstract class DaoSupport{
public void save(Object entity){}
public void delete(Object id){}
}
public class PersonServiceBean extends Daosupport{
}
实现关系实际上就是A类实现B类(接口
),他是依赖关系的特例
public interface PersonService{
public void delete(Interger id);
}
public class PersonServiceBean implements PersonService{
public void delete(Interger id){}
}
聚合关系表示的是整体和部分的关系,整体和部分可以分开
public class Computer{
private Mouse mouse;
private Moniter moniter;
public void setMouse(Mouse mouse){
this.mouse=mouse;
}
public void setMonitor(Moniter moniter){
this.moniter=moniter;
}
}
组合关系表示的也是整体和部分的关系,但是整体和部分不可以分开
如果我们认为Mouse,Monitor和Computer是不可分离的,则升级为组合关系
public class Computer{
private Mouse mouse = new Mouse();
private Moniter moniter = new Moniter();
}
Iterator模式用于在数据集合中按照顺序遍历集合。
lterator(迭代器)
该角色负责定义按顺序逐个遍历元素的接口(API)。
Concretelterator(具体的迭代器)
该角色负责实现Iterator角色所定义的接口(API)。
Aggregate(集合)
该角色负责定义创建Iterator角色的接口(API)。这个接口(API)是一个方法,会创建出“按顺序访问保存在我内部元素的人”。
ConcreteAggregate(具体的集合)
该角色负责实现Aggregate角色所定义的接口(API)。它会创建出具体的Iterator角色,即Concretelterator角色。
适配器用于填补需求和实际情况的空白,当我们不是从空白开始编程,需要对原有接口重复利用时,可以利用适配器模式,它可以很方便地利用原有接口创建新的接口。
适配器有两种方式: ①类适配器模式(使用继承的适配器)②对象适配器模式(使用委托的适配器)
类适配器模式的类图
对象适配器模式的类图
Target(对象)
该角色负责定义所需的方法。
Client(请求者)
该角色负责使用Target角色所定义的方法进行具体处理。
Adaptee(被适配)
注意不是Adapt-er(适配)角色,而是Adapt-ee(被适配)角色。Adaptee是一个持有既定方法的角色。如果Adaptee角色中的方法与Target角色的方法相同,就不需要接下来的Adapter角色了。
Adapter(适配)
Adapter模式的主人公。使用Adaptee角色的方法来满足Target角色的需求,这是Adapter模式的目的,也是Adapter角色的作用。
在类适配器模式中,Adapter角色通过继承来使用Adaptee角色,而在对象适配器模式中,Adapter角色通过委托来使用Adaptee角色。
Template Method模式是带有模板功能的模式,组成模板的方法被定义在父类中。由于这些方法是抽象方法,所以只查看父类的代码是无法知道这些方法最终会进行何种具体处理的,唯一能知道的就是父类是如何调用这些方法的。
实现上述这些抽象方法的是子类。在子类中实现了抽象方法也就决定了具体的处理。也就是说,只要在不同的子类中实现不同的具体处理,当父类的模板方法被调用时程序行为也会不同。但是,不论子类中的具体实现如何,处理的流程都会按照父类中所定义的那样进行。
像这样在父类中定义处理流程的框架,在子类中实现具体处理的模式成为Template Method模式。
AbstractClass(抽象类)
AbstractClass角色不仅负责实现模板方法,还负责声明在模板方法中所使用到的抽象方法。这些抽象方法由子类ConcreteClass角色负责实现。
ConcreteClass(具体类)
该角色负责具体实现AbstractClass角色中定义的抽象方法。这里实现的方法将会在AbstractClass角色的模板方法中被调用。
在Factory Method模式中,父类决定实例的生成方式,但并不决定所要生成的具体的类,具体的处理全部交给子类负责。这样就可以将生成实例的框架(framework)和实际负责生成实例的类解耦。
Product(产品)
Product角色属于框架这一方,是一个抽象类。它定义了在Factory Method模式中生成的那些实例所持有的接口(API),但具体的处理则由子类ConcreteProduct角色决定。
Creator(创建者)
Creator角色属于框架这一方,它是负责生成Product角色的抽象类,但具体的处理则由子类ConcreteCreator角色决定。
Creator 角色对于实际负责生成实例的ConcreteCreator角色一无所知,它唯一知道的就是,只要调用Product角色和生成实例的方法(图中的factoryMethod方法),就可以生成Product的实例。不用new关键字来生成实例,而是调用生成实例的专用方法来生成实例,这样就可以防止父类与其他具体类耦合。
ConcreteProduct(具体的产品)
ConcreteProduct 角色属于具体加工这一方,它决定了具体的产品。
ConcreteCreator(具体的创建者)
ConcreteCreator角色属于具体加工这一方,它负责生成具体的产品。
确保只生成一个实例的模式被称作Singleton模式。
Singleton类的构造函数是Private的,这是为了禁止从singleton类外部调用构造函数。如果从singleton类以外的代码中调用构造函数new singleton(),就会出现编译错误。
"-"表示private方法和字段;“+”表示public方法和字段;下划线表示static方法和字段
Singleton
在Singleton模式中,只有Singleton这一个角色。Singleton角色中有一个返回唯一实例的static方法。该方法总是会返回同一个实例。
Prototype模式根据现有实例来生成新实例。
以下情况不能根据类来生成实例,而要根据现有的实例来生成新的实例,此时应使用原型模式对类进行复制:
① 对象种类繁多,无法将它们整合到一个类中;② 难以根据类生成实例;③ 想解耦框架与生成的实例时
Prototype(原型)
Product角色负责定义用于复制现有实例来生成新实例的方法。
ConcretePrototype(具体的原型)
ConcretePrototype角色负责实现复制现有实例并生成新实例的方法。
Client(使用者)
Client角色负责使用复制实例的方法生成新的实例。
在java中如果想要调用clone方法,被复制的类必须实现java.lang.Clonable这个标记接口。clone是浅复制,只是将复制实例的字段值直接复制到新的实例中,并不会调用被复制实例的构造函数。
Builder模式用于组装具有复杂结构的实例
Builder模式的类图:
Builder模式的时序图:
Builder(建造者)
Builder角色负责定义用于生成实例的接口(API)。Builder角色中准备了用于生成实例的方法。
ConcreteBuilder(具体的建造者)
ConcreteBuilder角色是负责实现Builder角色的接口的类(API)。这里定义了在生成实例时实际被调用的方法。此外,在ConcreteBuilder角色中还定义了获取最终生成结果的方法。
Director(监工)
Director角色负责使用Builder角色的接口(API)来生成实例。它并不依赖于ConcreteBuilder角色。为了确保不论ConcreteBuilder角色是如何被定义的,Director角色都能正常工作,它只调用在Builder角色中被定义的方法。
Client(使用者)
该角色使用了Builder模式。
抽象工厂模式:提供一个创建一系列相关或相互依赖对象的接口,而无须指定它们具体的类。
抽象工厂模式中的具体工厂不只是创建一种产品,它负责创建一族产品。当系统所提供的工厂生产的具体产品并不是一个简单的对象,而是多个位于不同产品等级结构、属于不同类型的同一族产品时就可以使用抽象工厂模式。
AbstractProduct(抽象产品)
AbstractProduct角色负责定义AbstractFactory角色所生成的抽象零件和产品的接口(API)。
AbstractFactory(抽象工厂)
AbstractFactory角色负责定义用于生成抽象产品的接口(API)。
Client(委托者)
Client角色仅会调用AbstractFactory角色和AbstractProduct角色的接口(API)来进行工作,对于具体的零件、产品和工厂一无所知。图中省略了该角色
ConcreteProduct(具体产品)
ConcreteProduct 角色负责实现AbstractProduct角色的接口(API)。
ConcreteFactory(具体工厂)
ConcreteFactory角色负责实现AbstractFactory角色的接口(API)
抽象工厂模式的优缺点:
①易于增加具体的工厂,用于生产新的产品族
在Abstract Factory模式中增加具体的工厂是非常容易的。这里说的“容易”指的是需要编写哪些类和需要实现哪些方法都非常清楚。这样一来,无论要增加多少个具体工厂(或是要修改具体工厂的Bug),都无需修改抽象工厂和Main部分。
②难以在一个产品族中增加新的产品
需要修改所有的工厂角色,包括抽象工厂类,在所有的工厂类中都需要增加生产新产品的方法,违背了开闭原则。
1、简单工厂模式:
定义了一个创建对象的类,由这个类来封装实例化对象的行为,即create方法。
简单工厂模式的类图
2、工厂方法模式:
定义了一个创建对象的抽象方法,由子类决定要实例化的类。工厂方法模式将对象的实例化推迟到子类。
3、抽象工厂模式:
① 定义了一个interface用于创建相关或有依赖关系的对象簇
,而无需指明具体的类。
② 抽象工厂模式可以将简单工厂模式和工厂方法模式进行整合。
③抽象工厂模式将工厂抽象成两层:AbsFactory(抽象工厂)和具体实现的工厂子类,每个工厂子类可以生产一系列产品。
4、抽象工厂模式与工厂方法模式的对比:
Bridge模式的作用是将类的功能层次结构
和类的实现层次结构
连接起来,即在“类的功能层次结构”和“类的实现层次结构”之间搭建桥梁。
类的功能层次结构:
假设现在有一个类Something。当我们想在something中增加新功能时(想增加一个具体方法时),会编写一个something类的子类(派生类),即SomethingGood类。这样就构成了一个小小的类层次结构。
父类具有基本功能
在子类中增加新的功能
以上这种层次结构被称为类的功能层次结构。
如果我们要继续在SomethingGood类的基础上增加新的功能,该怎么办呢?这时,我们可以同样地编写一个SomethingGood类的子类,即SomethingBetter类。这样,类的层次结构就加深了。
类的功能层次结构即对类进行纵向扩展,但是类的功能层次结构不应当过深。
类的实现层次结构:
当子类ConcreteClass实现了父类AbstractClass类的抽象方法时,它们之间就构成了一个小小的层次结构。
但是,这里的类的层次结构并非用于增加功能,也就是说,这种层次结构并非用于方便我们增加新的方法。它的真正作用是帮助我们实现下面这样的任务分担:
父类通过声明抽象方法来定义接口(APl)
子类通过实现具体方法来实现接口(APl)
这种层次结构被称为类的实现层次结构。
当我们以其他方式实现AbstractClass时,例如要实现一个AnotherConcreteClass时,类的层次结构会稍微发生一些变化。
为了一种新的实现方式,我们继承了AbstractClass的子类,并实现了其中的抽象方法。
类的实现层次结构即对类进行横向扩展。
Bridge模式类图:
Abstraction(抽象化)
该角色位于“类的功能层次结构”的最上层。它使用Implementor角色的方法定义了基本的功能。该角色中保存了Implementor角色的实例。
RefinedAbstraction(改善后的抽象化)
在Abstraction角色的基础上增加了新功能的角色。
Implementor(实现者)
该角色位于“类的实现层次结构”的最上层。它定义了用于实现Abstraction角色的接口(API)的方法。
Concretelmplementor(具体实现者)
该角色负责实现在Implementor角色中定义的接口(API)。
在类图中,左侧的两个类构成了“类的功能层次结构”,右侧两个类构成了“类的实现层次结构”。类的两个层次结构之间的桥梁是impl
字段。
总结:
Bridge模式的特征是将“类的功能层次结构”与“类的实现层次结构”分离开了。将类的这两个层次结构分离开有利于独立地对它们进行扩展。
当想要增加功能时,只需要在“类的功能层次结构”一侧增加类即可,不必对“类的实现层次结构”做任何修改。而且,增加后的功能可以被“所有的实现”使用。
策略模式能够整体地替换算法的实现部分,能让我们以不同的算法来解决同一个问题。
Strategy(策略)
Strategy角色负责决定实现策略所必需的接口(API)。
ConcreteStrategy(具体的策略)
ConcreteStrategy角色负责实现Strategy角色的接口(API),即负责实现具体的策略(战略、方向、方法和算法)。
Context(上下文)
负责使用Strategy角色。Context角色保存了ConcreteStrategy角色的实例,并使用ConcreteStrategy角色去实现需求(总之,还是要调用Strategy角色的接口(API))。
将容器和内容作为同一种东西看待,可以帮助我们方便地处理问题。在容器中既可以放入内容,又可以放入小容器,然后在那个小容器中又可以放入更小的容器。这样,就形成了容器结构、递归结构。
能够使容器与内容具有一致性,创造出递归结构(树形结构) 的模式就是Composite模式。
Leaf(树叶)
表示“内容”的角色。在该角色中不能放入其他对象。
Composite(复合物)
表示容器的角色。可以在其中放入Leaf角色和Composite角色。
Component
使Leaf角色和Composite角色具有一致性的角色。Composite角色是Leaf角色和Composite角色的父类。
Client
使用Composite模式的角色。
装饰者模式:动态的将新功能附加到对象上。在对象功能扩展方面,它比继承更有弹性,装饰者模式也体现了开闭原则(ocp)。
Component
增加功能时的核心角色。Component角色只是定义了被装饰对象的接口(API)。
ConcreteComponent
该角色是实现了Component角色所定义的接口(API)的具体被装饰对象。
Decorator(装饰物)
该角色具有与Component角色相同的接口(API)。在它内部保存了被装饰对象Component角色。Decorator角色知道自己要装饰的对象。
ConcreteDecorator(具体的装饰物)
该角色是具体的Decorator角色。
1)访问者模式(Visitor Pattern),封装一些作用于某种数据结构的各元素的操作,它可以在不改变数据结构的前提下定义作用于这些元素的新的操作。
2)主要将数据结构与数据操作分离,解决数据结构
和操作
耦合性问题
3)访问者模式的基本工作原理是:在被访问的类里面加一个对外提供接待访问者的接口(增加accept方法,参数是访问者visitor类)
4)访问者模式主要应用场景是:需要对一个对象结构中的对象进行很多不同操作(这些操作彼此没有关联),同时需要避免让这些操作“污染“这些对象的类,可以选用访问者模式解决
Visitor(访问者)
Visitor角色负责对数据结构中每个具体的元素(ConcreteElement角色)声明一个用于访问XXXXX的visit(XXXXX)方法。visit(XXXXX)是用于处理XXXXX的方法,负责实现该方法的是ConcreteVisitor角色。
ConcreteVisitor(具体的访问者)
ConcreteVisitor角色负责实现Visitor角色所定义的接口(API)。它要实现所有的visit(XXXXX)方法,即实现如何处理每个ConcreteElement角色。随着visit(XXXXX)处理的进行,ConcreteVisitor角色的内部状态也会不断地发生变化。
Element(元素)
Element角色表示Visitor角色的访问对象。它声明了接受访问者的accept方法。accept方法接收到的参数是Visitor角色。
ConcreteElement
ConcreteElement角色负责实现Element角色所定义的接口(API)。
ObjectStructure(对象结构)
ObjectStructur角色负责处理Element角色的集合。
访问者模式的缺点:(1) 具体元素对访问者公布细节,也就是说访问者关注了其他类的内部细节,这是迪米特法则所不建议的,这样造成了具体元素变更比较困难。(2) 违背了依赖倒转原则。访问者依赖的是具体元素,而不是抽象元素
将多个对象组成一条职责链,然后按照它们在职责链上的顺序一个一个地找出到底应该谁来负责处理。
使用职责链模式可以弱化“请求方”和“处理方”之间的关联关系,让双方各自都成为可独立复用的组件。
使用职责链模式确实会导致处理请求发生了延迟;如果请求和处理者之间的关系是确定的,而且需要非常快的处理速度时,不使用职责链模式会更好。
Handler(处理者)
Handler角色定义了处理请求的接口(API)。Handler角色知道“下一个处理者”是谁,如果自己无法处理请求,它会将请求转给“下一个处理者”。当然,“下一个处理者”也是Handler角色。
ConcreteHandler(具体的处理者)
ConcreteVisitor角色是处理请求的具体角色。
Client(请求者)
Client角色是向第一个ConcreteHandler角色发送请求的角色。
外观模式的引入:一个影院管理项目
组件一个家庭影院:
DVD播放器、投影仪、自动屏幕、环绕立体声、爆米花机,要求完成使用家庭影院的功能,其过程为:
1、开爆米花机
2、放下屏幕
3、开投影仪
4、开音响
5、开DVD,选dvd
6、去拿爆米花
7、调暗灯光
8、播放
9、投影结束后,关闭各种设备
传统方式解决影院管理:
传统方式解决影院管理问题分析:
1)在ClientTest的main方法中,创建各个子系统的对象,并直接去调用子系统(对象)相关方法,会造成调用过程混乱,没有清晰的过程
2)不利于在ClientTest中,去维护对子系统的操作
3)解决思路:定义一个高层接口,给子系统中的一组接口提供一个一致的界面,用来访问子系统中的一群接口
4)也就是说
通过定义一个一致的接口(界面类),用以屏蔽内部子系统的细节,使得调用端只需跟这个接口发生调用,而无需关系这个系统的内部细节 => 外观模式
外观模式基本介绍:
1)外观模式(Facade),也叫“过程模式”:外观模式为子系统中的一组接口提供一个一致的界面,此模式定义了一个高层接口,这个接口与使得这一子系统更加容易使用
2)外观模式通过定义一个一致的接口,用以屏蔽子系统的细节,使用调用端只需跟这个接口发生调用,而无需关系这个子系统的内部细节
外观模式类图:
Facade(窗口)
Facade角色是代表构成系统的许多角色的“简单窗口”。Facade角色向系统外部提供高层接口(API)。
构成系统的许多其他角色
这些角色各自完成自己的工作,它们并不知道Facade角色。Facade角色调用其他角色进行工作,但是其他角色不会调用Facade角色。这里是类图中的各个子系统,他们是功能的实际提供者。
Client(请求者)
Client角色负责调用Facade角色。
Facade模式可以让复杂的东西看起来简单,它使得接口(API)变少了,意味着程序与外部的关联关系弱化了,这样更容易使我们的包作为组件被复用。
基本介绍
类图
Mediator(仲裁者、中介者)
Mediator角色负责定义与Colleague角色进行通信和做出决定的接口(API)。
ConcreteMediator(具体的仲裁者、中介者)
ConcreteMediator角色负责实现Mediator角色的接口(API),负责实际做出决定。他需要知道所有具体的同事类,并接受某个同事对象消息,完成相应的任务。
Colleague(同事)
Colleague角色负责定义与Mediator角色进行通信的接口(API)。
ConcreteColleague(具体的同事)
ConcreteColleague角色负责实现Colleague角色的接口(API)。每个同事只知道自己的行为,而不了解其他同事类的行为,但是他们都依赖中介者对象。
中介者模式注意事项和细节:
1)多个类相互耦合,会形成网状结构,使用中介者模式将网状结构分离为星型结构,进行解耦
2)减少类间依赖,降低了耦合,符合迪米特原则
3)中介者承担了较多的责任,一旦中介者出现了问题,整个系统就会受到影响
4)如果设计不当,中介者对象本身变得过于复杂,这点在实际使用时,要特别注意
在观察者模式中,当观察对象的状态发生变化时,会通知给观察者。观察者模式适用于根据对象状态进行相应处理的场景。
Subject(观察对象)
Subject角色表示观察对象。Subject角色定义了注册观察者(addObserver
)和删除观察者(deleteObserver
)的方法。此外,它还声明了“获取现在的状态”(getSubjectStatus
)的方法。
ConcreteSubject(具体的观察对象)
ConcreteSubject角色表示具体的被观察对象。当自身状态发生变化后,它会通知所有已经注册的Observer角色(notifyObservers
)。
Observer(观察者)
Observer角色负责接收来自Subject角色的状态变化的通知。为此,它声明了update
方法。
ConcreteObserver(具体的观察者)
ConcreteObserver角色表示具体的Observer。当它的update
方法被调用后,会去获取要观察的对象的最新状态。
备忘录模式(Memento Pattern) 在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到原先保存的状态.
备忘录模式事先将某个时间点的实例的状态
保存下来,之后在有必要时,再将实例恢复至当时的状态
。
Originator(生成者)
Originator角色会在保存自己的最新状态时生成Memento角色。当把以前保存的Memento角色传递给Originator角色时,它会将自己恢复至生成该Memento角色时的状态。
Memento(备忘录)
Memento角色会将Originator角色的内部信息整合在一起。在Memento角色中虽然保存了Originator角色的信息,但它不会向外部公开这些信息。
Memento角色有以下两种接口(APl)。
wide interface——宽接口(API)
Memento角色提供的“宽接口(API)”是指所有用于获取恢复对象状态信息的方法的集合。由于宽接口(API)会暴露所有Memento角色的内部信息,因此能够使用宽接口(API)的只有Originator角色。
narrowinterface——窄接口(API)
Memento角色为外部的Caretaker角色提供了“窄接口(API)”。可以通过窄接口(API)获取的Memento角色的内部信息非常有限,因此可以有效地防止信息泄露。
通过对外提供以上两种接口(API),可以有效地防止对象的封装性被破坏。
Originator角色和Memento角色之间有着非常紧密的联系。
Caretaker(负责人)
当Caretaker角色想要保存当前的Originator角色的状态时,会通知Originator角色。Originator角色在接收到通知后会生成Memento角色的实例并将其返回给Caretaker角色。由于以后可能会用Memento 实例来将Originator恢复至原来的状态,因此Caretaker角色会一直保存Memento实例。
不过,Caretaker角色只能使用Memento角色两种接口(API)中的窄接口(API),也就是说它无法访问Memento角色内部的所有信息。它只是将Originator角色生成的Memento角色当作一个黑盒子保存起来。
虽然 Originator角色和Memento角色之间是强关联关系,但Caretaker角色和Memento角色之间是弱关联关系。Memento角色对Caretaker角色隐藏了自身的内部信息。
在State模式中,我们用类来表示状态。以类来表示状态后,我们就能通过切换类来方便地改变对象的状态。
状态模式(State Pattern)主要用来解决对象在多种状态转换时,需要对外输出不同的行为的问题。状态和行为是一一对应的,状态之间可以相互转换
当一个对象的内在状态改变时,允许改变其行为,这个对象看起来像是改变了其类
State(状态)
State角色表示状态,定义了根据不同状态进行不同处理的接口(API)。该接口(API)是那些处理内容依赖于状态的方法的集合
。
ConcreteState(具体状态)
ConcreteState角色表示各个具体的状态,它实现了state接口。
Context(状况、前后关系、上下文)
Context角色持有表示当前状态的ConcreteState角色。此外,它还定义了供外部调用者使用State 模式的接口(API)。给代表状态的字段赋予表示当前状态的类的实例,就相当于进行了状态转移。
基本介绍
1)享元模式:当需要某个实例时,并不总是通过new关键字来生成实例,而是尽量共用已经存在的实例。即通过尽量共享实例来避免new出实例。
2)享元模式能够解决重复对象的内存浪费的问题,当系统中有大量相似对象,需要缓冲池时。不需总是创建新对象,可以从缓冲池里拿。这样可以降低系统内存,同时提高效率
3)外部状态与内部状态:
内部状态 (Intrinsic State):存储在享元对象内部并且不会随环境改变而改变的状态,内部状态可以共享
外部状态(Extrinsic State):随环境改变而改变的、不可以共享的状态。享元对象的外部状态通常由客户端保存,并在享元对象被创建之后,需要使用的时候再传入到享元对象内部。一个外部状态与另一个外部状态之间是相互独立的
4)原理:
①将具有相同内部状态的对象存储在享元池中,享元池中的对象是可以实现共享的
②需要的时候将对象从享元池中取出,即可实现对象的复用
③通过向取出的对象注入不同的外部状态,可以得到一系列相似的对象,而这些对象在内存中实际上只存储一份
④ 享元模式中外部状态相对独立,而且不会影响其内部状态,从而使得享元对象可以在不同的环境中被共享
5)常用于系统底层开发,解决系统的性能问题。像数据库连接池
,里面都是创建好的连接对象,在这些连接对象中有我们需要的则直接拿来用,避免重新创建,如果没有我们需要的,则创建一个。
享元模式经典的应用场景就是池技术了,String常量池、数据库连接池、缓冲池等等都是享元模式的应用,享元模式是池技术的重要实现方式
类图
代码实现:
抽象享元类:
public abstract class Flyweight {
public abstract void operation(String extrinsicState);
}
具体享元类:
public class ConcreteFlyweight extends Flyweight {
//内部状态intrinsicState作为成员变量,同一个享元对象其内部状态是一致的
private String intrinsicState;
public ConcreteFlyweight(String intrinsicState) {
this.intrinsicState = intrinsicState;
}
//外部状态extrinsicState在使用时由外部设置,不保存在享元对象中,即使是同一个对象,在每一次调用时可以传入不同的外部状态
public void operation(String extrinsicState) {
//实现业务方法
}
}
非共享具体享元类:
public class UnsharedConcreteFlyweight extends Flyweight {
public void operation(String extrinsicState) {
//实现业务方法
}
}
享元工厂类:
public class FlyweightFactory {
//定义一个HashMap用于存储享元对象,实现享元池
private HashMap flyweights = new HashMap();
public Flyweight getFlyweight(String key) {
//如果对象存在,则直接从享元池获取
if (flyweights.containsKey(key)) {
return (Flyweight)flyweights.get(key);
}
//如果对象不存在,先创建一个新的对象添加到享元池中,然后返回
else {
Flyweight fw = new ConcreteFlyweight();
flyweights.put(key,fw);
return fw;
}
}
}
代理模式:
为一个对象提供一个替身,以控制对这个对象的访问,即通过代理对象访问目标对象。这样做的好处是:可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能
。
代理人只代理他能解决的问题。当遇到他不能解决的问题时,还是会 “转交” 给本人去解决。另外,使用proxy可以提升处理速度,将耗时处理推迟至需要时才交给本人去处理。
虚拟代理: 如果需要创建一个资源消耗较大的对象,先创建一个消耗相对较小的对象来表示,真实对象只在需要时才会被真正创建。虚拟代理能够降低系统开销、缩短运行时间。
保护代理: 控制对一个对象的访问,可以给不同的用户提供不同级别的使用权限。
缓冲代理: 为某一个目标操作的结果提供临时的存储空间,以便多个客户端可以共享这些结果。
智能引用代理: 当一个对象被引用时,提供一些额外的操作,例如将对象被调用的次数记录下来等。
Subject(主体)
Subject角色定义了使Proxy角色和RealSubject角色之间具有一致性的接口。由于存在Subject角色,所以Client角色不必在意它所使用的究竟是Proxy角色还是RealSubject角色。
Proxy(代理人)
Proxy角色会尽量处理来自Client角色的请求。**只有当自己不能处理时,它才会将工作交给RealSubject角色。**Proxy角色只有在必要时才会生成RealSubject角色。Proxy角色实现了在Subject角色中定义的接口(API)。
RealSubject(实际的主体)
“本人” RealSubject角色会在 “代理人” Proxy角色无法胜任工作时出场。它与Proxy角色一样,也实现了在Subject角色中定义的接口(API)。
Client(请求者)
使用Proxy模式的角色。
1)命令模式(Command Pattern):在软件设计中,我们经常需要向某些对象发送请求,但是并不知道请求的接收者是谁,也不知道被请求的操作是哪个,我们只需在程序运行时指定具体的请求接收者即可,此时,可以使用命令模式来进行设计
2)命名模式使得请求发送者与请求接收者消除彼此之间的耦合,让对象之间的调用关系更加灵活,实现解耦。
3)在命名模式中,会将一个请求封装为一个对象,以便使用不同参数来表示不同的请求,同时命令模式也支持可撤销的操作
4)通俗易懂的理解:将军发布命令,士兵去执行。其中有几个角色:将军(命令发布者)、士兵(命令的具体执行者)、命令(连接将军和士兵)。
Invoker是调用者(将军),Receiver是被调用者(士兵),MyCommand是命令,实现了Command接口,持有接收对象
Command(命令)
Command角色负责定义命令的接口(API)。
Invoker(发动者)
Invoker角色是开始执行命令的角色,它会调用在Command角色中定义的接口(API)。
Receiver(接收者)
Receiver角色是Command角色执行命令时的对象,也可以称其为命令接收者。知道如何实施和执行一个请求相关的操作。
ConcreteCommand(具体的命令)
ConcreteCommand角色负责实现在Command角色中定义的接口(API)。它将一个Receiver对象和一个动作绑定调用Receiver相应的操作,实现execute。
Client(请求者)
Client角色负责生成ConcreteCommand角色并分配Receiver角色。
Invoker和Receiver之间通过Command进行解耦。
命令模式的注意事项和细节:
1)将发起请求的对象与执行请求的对象解耦。发起请求的对象是调用者,调用者只要调用命令对象的execute()方法就可以让接收者工作,而不必知道具体的接收者对象是谁、是如何实现的,命令对象会负责让接收者执行请求的动作,也就是说:”请求发起者”和“请求执行者”之间的解耦是通过命令对象实现的,命令对象起到了纽带桥梁的作用。
2)容易设计一个命令队列。只要把命令对象放到列队,就可以多线程的执行命令
3)容易实现对请求的撤销和重做
4)命令模式不足:可能导致某些系统有过多的具体命令类,增加了系统的复杂度,这点在在使用的时候要注意
5)空命令
也是一种设计模式,它为我们省去了判空的操作。
6)命令模式经典的应用场景:界面的一个按钮都是一条命令、模拟CMD(DOS命令)订单的撤销/恢复、触发-反馈机制
基本介绍:
1)在编译原理中,一个算术表达式通过词法分析器形成词法单元,而后这些词法单元再通过语法分析器构建语法分析树,最终形成一颗抽象的语法分析树。这里的词法分析器和语法分析器都可以看做是解释器
2)解释器模式(Interpreter Pattern):是指给定一个语言(表达式),定义它的文法的一种表示,并定义一个解释器,使用该解释器来解释语言中的句子(表达式)
3)应用场景
应用可以将一个需要解释执行的语言中的句子表示为一个抽象语法树
一些重复出现的问题可以用一种简单的语言来表达
一个简单语法需要解释的场景
4)这样的例子还有,比如编译器
、运算表达式计算
、正则表达式
、机器人
等
类图:
AbstractExpression(抽象表达式)
AbstractExpression角色定义了语法树节点的共同接口(API),类图中是interpreter
。
TerminalExpression(终结符表达式)
TerminalExpression 角色实现与文法中的终结符相关的解释操作。
NonterminalExpression(非终结符表达式)
NonterminalExpression角色为文法中的非终结符实现解释操作。
Context(文脉、上下文)
Context角色为解释器进行语法解析提供了必要的信息。
Client(请求者)
为了推导语法树,Client角色会调用TerminalExpression角色和NonterminalExpresion角色。
解释器模式的注意事项和细节:
1)解释器模式要慎用,难度比较大
2)当有一个语言需要解释执行,可将该语言中的句子表示为一个抽象语法树,就可以考虑使用解释器模式,让程序具有良好的扩展性
3)使用解释器可能带来的问题:解释器模式会引起类膨胀、解释器模式采用递归调用方法,将会导致调试非常复杂、效率可能降低
因篇幅问题不能全部显示,请点此查看更多更全内容