设计模式(GOF)分为三种类型,共23类。
1、单例模式 (Singleton)
2、原型模式 (Prototype)
简单工厂模式 (SimpleFactory Mode)
3、工厂方法模式 (Factory Method)
4、抽象工厂模式 (Abstract Factory)
5、建造者模式 (Builder)
6、代理模式 (Proxy)
7、适配器模式 (Adapter)
8、桥接模式 (Bridge)
9、装饰器模式 (Decorator)
10、外观模式 (Facade)
11、享元模式 (Flyweight)
12、组合模式 (Composite)
13、模板方法模式 (Template Method)
14、策略模式 (Strategy)
15、命令模式 (Command)
16、责任链模式 (Chain of Responsibility)
17、状态模式 (State)
18、观察者模式 (Observer)
19、中介者模式 (Mediator)
20、迭代器模式 (Iterator)
21、访问者模式 (Visitor)
22、备忘录模式 (Memento)
23、解释器模式 (Interpreter)
设计模式(GOF)分为三种类型,共23类。
一、创建型模式:
单例模式、抽象工厂模式、建造者模式、工厂模式、原型模式。
二、结构型模式:
适配器模式、桥接模式、装饰模式、组合模式、外观模式、享元模式、代理模式。
三、行为型模式:
模版方法模式、命令模式、迭代器模式、观察者模式、中介者模式、备忘录模式、解释器模式、状态模式、策略模式、职责链模式、访问者模式。
1、单例模式 (Singleton)
保证一个类仅有一个实例,且该类能自行创建这个实例,并提供一个访问它的全局访问点。单例模式是最简单的设计模式之一。
特点:
单例类只有一个实例对象;
该单例对象必须由单例类自行创建;
单例类对外提供一个访问该单例的全局访问点。
单例模式的优缺点:
优点:
单例模式可以保证内存里只有一个实例,减少了内存的消耗;
可以避免对资源的多重占用;
单例模式设置全局访问点,可以优化和共享资源的访问。
缺点:
单例模式一般没有接口,扩展困难。如果要扩展,则除了修改原来的代码,没有第二种途径,违背开闭原则;
在并发测试中,单例模式不利于代码调试。在调试过程中,如果单例中的代码没有执行完,也不能模拟生成一个新的对象;
单例模式的功能代码通常写在一个类中,如果功能设计不合理,则很容易违背单一职责原则。
单例模式的应用场景:
需要频繁创建的一些类,使用单例可以降低系统的内存压力,减少GC(垃圾回收);
某类只要求生成一个对象的时候,如一个班中的班长、每个人的身份证号等;
某些类创建实例时占用资源较多,或实例化耗时较长,且经常使用;
某类需要频繁实例化,而创建的对象又频繁被销毁的时候,如多线程的线程池、网络连接池等;
频繁访问数据库或文件的对象;
对于一些控制硬件级别的操作,或者从系统上来讲应当是单一控制逻辑的操作,如果有多个实例,则系统会完全乱套;
当对象需要被共享的场合。由于单例模式只允许创建一个对象,共享该对象可以节省内存,并加快对象访问速度。如Web中的配置对象、数据库的连接池等。
单例模式的实现:
懒汉式:该模式的特点是类加载时没有生成单例,只有当第一次调用 getlnstance 方法时才去创建这个单例。代码如下:
public class LazySingleton {
private static volatile LazySingleton instance = null; //保证 instance 在所有线程中同步
private LazySingleton() {
} //private 避免类在外部被实例化
public static synchronized LazySingleton getInstance() {
//getInstance 方法前加同步
if (instance == null) {
instance = new LazySingleton();
}
return instance;
}
}
如果编写的是多线程程序,则不要删除上例代码中的关键字 volatile 和 synchronized,否则将存在线程非安全的问题。如果不删除这两个关键字就能保证线程安全,但是每次访问时都要同步,会影响性能,且消耗更多的资源,这是懒汉式单例的缺点。
饿汉式:该模式的特点是类一旦加载就创建一个单例,保证在调用 getInstance 方法之前单例已经存在了。
public class HungrySingleton {
private static final HungrySingleton instance = new HungrySingleton();
private HungrySingleton() {
}
public static HungrySingleton getInstance() {
return instance;
}
}
饿汉式单例在类创建的同时就已经创建好一个静态的对象供系统使用,以后不再改变,所以是线程安全的,可以直接用于多线程而不会出现问题。
2、原型模式 (Prototype)
用原型实例指定创建对象的种类,并且通过复制这个原型来创建一个和原型相同或相似的新对象。
原型的优缺点:
优点:
自带的原型模式基于内存二进制流的复制,在性能上比直接 new 一个对象更加优良;
可以使用深克隆方式保存对象的状态,使用原型模式将对象复制一份,并将其状态保存起来,简化了创建对象的过程,以便在需要的时候使用(例如恢复到历史某一状态),可辅助实现撤销操作。
缺点:
需要为每一个类都配置一个 clone 方法;
clone 方法位于类的内部,当对已有类进行改造的时候,需要修改代码,违背了开闭原则;
当实现深克隆时,需要编写较为复杂的代码,而且当对象之间存在多重嵌套引用时,为了实现深克隆,每一层对象对应的类都必须支持深克隆,实现起来会比较麻烦。因此,深克隆、浅克隆需要运用得当。
模式的结构:
原型模式包含以下主要角色:
抽象原型类:规定了具体原型对象必须实现的接口。
具体原型类:实现抽象原型类的 clone() 方法,它是可被复制的对象。
访问类:使用具体原型类中的 clone() 方法来复制新的对象。
模式的实现:
原型模式的克隆分为浅克隆和深克隆。
浅克隆:创建一个新对象,新对象的属性和原来对象完全相同,对于非基本类型属性,仍指向原有属性所指向的对象的内存地址。
深克隆:创建一个新对象,属性中引用的其他对象也会被克隆,不再指向原有对象地址。
Java 中的 Object 类提供了浅克隆的 clone() 方法,具体原型类只要实现 Cloneable 接口就可实现对象的浅克隆,这里的 Cloneable 接口就是抽象原型类。其代码如下:
//具体原型类
class Realizetype implements Cloneable {
Realizetype() {
System.out.println("具体原型创建成功!");
}
public Object clone() throws CloneNotSupportedException {
System.out.println("具体原型复制成功!");
return (Realizetype) super.clone();
}
}
//原型模式的测试类
public class PrototypeTest {
public static void main(String[] args) throws CloneNotSupportedException {
Realizetype obj1 = new Realizetype();
Realizetype obj2 = (Realizetype) obj1.clone();
System.out.println("obj1==obj2?" + (obj1 == obj2));
}
}
简单工厂模式 (SimpleFactory Mode)
定义一个工厂类,他可以根据参数的不同返回不同类的实例,被创建的实例通常都具有共同的父类。(它不属于23种模式里面的)
简单工厂的优缺点:
优点:
工厂类包含必要的逻辑判断,可以决定在什么时候创建哪一个产品的实例。客户端可以免除直接创建产品对象的职责,很方便的创建出相应的产品。工厂和产品的职责区分明确。
客户端无需知道所创建具体产品的类名,只需知道参数即可。
也可以引入配置文件,在不修改客户端代码的情况下更换和添加新的具体产品类。
缺点:
简单工厂模式的工厂类单一,负责所有产品的创建,职责过重,一旦异常,整个系统将受影响。且工厂类代码会非常臃肿,违背高聚合原则。
使用简单工厂模式会增加系统中类的个数(引入新的工厂类),增加系统的复杂度和理解难度。
系统扩展困难,一旦增加新产品不得不修改工厂逻辑,在产品类型较多时,可能造成逻辑过于复杂。
简单工厂模式使用了 static 工厂方法,造成工厂角色无法形成基于继承的等级结构。
简单工厂模式的结构与实现:
简单工厂模式的主要角色如下:
简单工厂(SimpleFactory):是简单工厂模式的核心,负责实现创建所有实例的内部逻辑。工厂类的创建产品类的方法可以被外界直接调用,创建所需的产品对象。
抽象产品(Product):是简单工厂创建的所有对象的父类,负责描述所有实例共有的公共接口。
具体产品(ConcreteProduct):是简单工厂模式的创建目标。
参考代码如下:
public class Client {
public static void main(String[] args) {
}
//抽象产品
public interface Product {
void show();
}
//具体产品:ProductA
static class ConcreteProduct1 implements Product {
public void show() {
System.out.println("具体产品1显示...");
}
}
//具体产品:ProductB
static class ConcreteProduct2 implements Product {
public void show() {
System.out.println("具体产品2显示...");
}
}
final class Const {
static final int PRODUCT_A = 0;
static final int PRODUCT_B = 1;
static final int PRODUCT_C = 2;
}
static class SimpleFactory {
public static Product makeProduct(int kind) {
switch (kind) {
case Const.PRODUCT_A:
return new ConcreteProduct1();
case Const.PRODUCT_B:
return new ConcreteProduct2();
}
return null;
}
}
}
3、工厂方法模式 (Factory Method)
定义一个用于创建对象的接口,让子类决定将哪一个类实例化。Factory Method使一个类的实例化延迟到其子类。
模式的优缺点:
优点:
用户只需要知道具体工厂的名称就可得到所要的产品,无须知道产品的具体创建过程;
灵活性增强,对于新产品的创建,只需多写一个相应的工厂类;
典型的解耦框架。高层模块只需要知道产品的抽象类,无须关心其他实现类,满足迪米特法则、依赖倒置原则和里氏替换原则。
缺点:
类的个数容易过多,增加复杂度;
增加了系统的抽象性和理解难度;
抽象产品只能生产一种产品,此弊端可使用抽象工厂模式解决。
模式的结构:
工厂方法模式的主要角色如下:
抽象工厂(Abstract Factory):提供了创建产品的接口,调用者通过它访问具体工厂的工厂方法 newProduct() 来创建产品;
具体工厂(ConcreteFactory):主要是实现抽象工厂中的抽象方法,完成具体产品的创建;
抽象产品(Product):定义了产品的规范,描述了产品的主要特性和功能;
具体产品(ConcreteProduct):实现了抽象产品角色所定义的接口,由具体工厂来创建,它同具体工厂之间一一对应。
具体的代码实现参考网上。
4、抽象工厂模式 (Abstract Factory)
是一种为访问类提供一个创建一组相关或相互依赖对象的接口,且访问类无须指定所要产品的具体类就能得到同族的不同等级的产品的模式结构。
抽象工厂模式的优缺点:
抽象工厂模式除了具有工厂方法模式的优点外,其他主要优点如下。
可以在类的内部对产品族中相关联的多等级产品共同管理,而不必专门引入多个新的类来进行管理。
当需要产品族时,抽象工厂可以保证客户端始终只使用同一个产品的产品组。
抽象工厂增强了程序的可扩展性,当增加一个新的产品族时,不需要修改原代码,满足开闭原则。
其缺点是:当产品族中需要增加一个新的产品时,所有的工厂类都需要进行修改。增加了系统的抽象性和理解难度。
模式的结构:
抽象工厂模式的主要角色如下。
抽象工厂(Abstract Factory):提供了创建产品的接口,它包含多个创建产品的方法 newProduct(),可以创建多个不同等级的产品。
具体工厂(Concrete Factory):主要是实现抽象工厂中的多个抽象方法,完成具体产品的创建。
抽象产品(Product):定义了产品的规范,描述了产品的主要特性和功能,抽象工厂模式有多个抽象产品。
具体产品(ConcreteProduct):实现了抽象产品角色所定义的接口,由具体工厂来创建,它同具体工厂之间是多对一的关系。
模式的实现:
(1) 抽象工厂:提供了产品的生成方法。
interface AbstractFactory {
public Product1 newProduct1();
public Product2 newProduct2();
}
(2) 具体工厂:实现了产品的生成方法。
class ConcreteFactory1 implements AbstractFactory {
public Product1 newProduct1() {
System.out.println("具体工厂 1 生成-->具体产品 11...");
return new ConcreteProduct11();
}
public Product2 newProduct2() {
System.out.println("具体工厂 1 生成-->具体产品 21...");
return new ConcreteProduct21();
}
}
5、建造者模式 (Builder)
指将一个复杂对象的构造与它的表示分离,使同样的构建过程可以创建不同的表示。它是将一个复杂的对象分解为多个简单的对象,然后一步一步构建而成。它将变与不变相分离,即产品的组成部分是不变的,但每一部分是可以灵活选择的。
模式的优缺点:
优点:
封装性好,构建和表示分离;
扩展性好,各个具体的建造者相互独立,有利于系统的解耦;
客户端不必知道产品内部组成的细节,建造者可以对创建过程逐步细化,而不对其它模块产生任何影响,便于控制细节风险。
缺点:
产品的组成部分必须相同,这限制了其使用范围。
如果产品的内部变化复杂,如果产品内部发生变化,则建造者也要同步修改,后期维护成本较大。
模式的结构:
建造者(Builder)模式的主要角色如下。
产品角色(Product):它是包含多个组成部件的复杂对象,由具体建造者来创建其各个零部件;
抽象建造者(Builder):它是一个包含创建产品各个子部件的抽象方法的接口,通常还包含一个返回复杂产品的方法 getResult();
具体建造者(Concrete Builder):实现 Builder 接口,完成复杂产品的各个部件的具体创建方法;
指挥者(Director):它调用建造者对象中的部件构造与装配方法完成复杂对象的创建,在指挥者中不涉及具体产品的信息。
模式的实现:
(1) 产品角色:包含多个组成部件的复杂对象。
class Product {
private String partA;
private String partB;
private String partC;
public void setPartA(String partA) {
this.partA = partA;
}
public void setPartB(String partB) {
this.partB = partB;
}
public void setPartC(String partC) {
this.partC = partC;
}
public void show() {
//显示产品的特性
}
}
(2) 抽象建造者:包含创建产品各个子部件的抽象方法。
abstract class Builder {
//创建产品对象
protected Product product = new Product();
public abstract void buildPartA();
public abstract void buildPartB();
public abstract void buildPartC();
//返回产品对象
public Product getResult() {
return product;
}
}
(3) 具体建造者:实现了抽象建造者接口。
public class ConcreteBuilder extends Builder {
public void buildPartA() {
product.setPartA("建造 PartA");
}
public void buildPartB() {
product.setPartB("建造 PartB");
}
public void buildPartC() {
product.setPartC("建造 PartC");
}
}
(4) 指挥者:调用建造者中的方法完成复杂对象的创建。
class Director {
private Builder builder;
public Director(Builder builder) {
this.builder = builder;
}
//产品构建与组装方法
public Product construct() {
builder.buildPartA();
builder.buildPartB();
builder.buildPartC();
return builder.getResult();
}
}
(5) 客户类。
public class Client {
public static void main(String[] args) {
Builder builder = new ConcreteBuilder();
Director director = new Director(builder);
Product product = director.construct();
product.show();
}
}
建造者模式主要适用于以下应用场景:
相同的方法,不同的执行顺序,产生不同的结果;
多个部件或零件,都可以装配到一个对象中,但是产生的结果又不相同;
产品类非常复杂,或者产品类中不同的调用顺序产生不同的作用;
初始化一个对象特别复杂,参数多,而且很多参数都具有默认值。
建造者模式和工厂模式的区别:
建造者模式更加注重方法的调用顺序,工厂模式注重创建对象;
创建对象的力度不同,建造者模式创建复杂的对象,由各种复杂的部件组成,工厂模式创建出来的对象都一样;
关注重点不一样,工厂模式只需要把对象创建出来就可以了,而建造者模式不仅要创建出对象,还要知道对象由哪些部件组成;
建造者模式根据建造过程中的顺序不一样,最终对象部件组成也不一样。
6、代理模式 (Proxy)
为其他对象提供一个代理以控制对这个对象的访问。
模式的优缺点:
优点:
代理模式在客户端与目标对象之间起到一个中介作用和保护目标对象的作用;
代理对象可以扩展目标对象的功能;
代理模式能将客户端与目标对象分离,在一定程度上降低了系统的耦合度,增加了程序的可扩展性。
缺点(其缺点可以用动态代理解决):
代理模式会造成系统设计中类的数量增加
在客户端和目标对象之间增加一个代理对象,会造成请求处理速度变慢;
增加了系统的复杂度。
模式的结构:
代理模式的主要角色如下。
抽象主题(Subject)类:通过接口或抽象类声明真实主题和代理对象实现的业务方法;
真实主题(Real Subject)类:实现了抽象主题中的具体业务,是代理对象所代表的真实对象,是最终要引用的对象;
代理(Proxy)类:提供了与真实主题相同的接口,其内部含有对真实主题的引用,它可以访问、控制或扩展真实主题的功能。
根据代理的创建时期,代理模式分为静态代理和动态代理。
静态:由程序员创建代理类或特定工具自动生成源代码再对其编译,在程序运行前代理类的 .class 文件就已经存在了。
动态:在程序运行时,运用反射机制动态创建而成。
模式的实现:
public class ProxyTest {
public static void main(String[] args) {
Proxy proxy = new Proxy();
proxy.Request();
}
}
//抽象主题
interface Subject {
void Request();
}
//真实主题
class RealSubject implements Subject {
public void Request() {
System.out.println("访问真实主题方法...");
}
}
//代理
class Proxy implements Subject {
private RealSubject realSubject;
public void Request() {
if (realSubject == null) {
realSubject = new RealSubject();
}
preRequest();
realSubject.Request();
postRequest();
}
public void preRequest() {
System.out.println("访问真实主题之前的预处理。");
}
public void postRequest() {
System.out.println("访问真实主题之后的后续处理。");
}
}
7、适配器模式 (Adapter)
将一个类的接口转换成客户希望的另外一个接口。Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。适配器模式分为类结构型模式和对象结构型模式两种,前者类之间的耦合度比后者高,且要求程序员了解现有组件库中的相关组件的内部结构,所以应用相对较少些。
模式的优缺点:
优点:
客户端通过适配器可以透明地调用目标接口;
复用了现存的类,程序员不需要修改原有代码而重用现有的适配者类;
将目标类和适配者类解耦,解决了目标类和适配者类接口不一致的问题;
在很多业务场景中符合开闭原则。
缺点:
适配器编写过程需要结合业务场景全面考虑,可能会增加系统的复杂性;
增加代码阅读难度,降低代码可读性,过多使用适配器会使系统代码变得凌乱。
模式的结构:
类适配器模式可采用多重继承方式实现,如 C++接口;Java不支持多继承,但可以定义一个适配器类来实现当前系统的业务接口,同时又继承现有组件库中已经存在的组件。
对象适配器模式可釆用将现有组件库中已经实现的组件引入适配器类中,该类同时实现当前系统的业务接口。
适配器模式(Adapter)包含以下主要角色。
目标(Target)接口:当前系统业务所期待的接口,它可以是抽象类或接口;
适配者(Adaptee)类:它是被访问和适配的现存组件库中的组件接口;
适配器(Adapter)类:它是一个转换器,通过继承或引用适配者的对象,把适配者接口转换成目标接口,让客户按目标接口的格式访问适配者。
模式的实现:
(1) 类适配器模式的代码如下。
package adapter;
//目标接口
interface Target
{
public void request();
}
//适配者接口
class Adaptee
{
public void specificRequest()
{
System.out.println("适配者中的业务代码被调用!");
}
}
//类适配器类
class ClassAdapter extends Adaptee implements Target
{
public void request()
{
specificRequest();
}
}
//客户端代码
public class ClassAdapterTest
{
public static void main(String[] args)
{
System.out.println("类适配器模式测试:");
Target target = new ClassAdapter();
target.request();
}
}
(2)对象适配器模式的代码如下。
package adapter;
//对象适配器类
class ObjectAdapter implements Target
{
private Adaptee adaptee;
public ObjectAdapter(Adaptee adaptee)
{
this.adaptee=adaptee;
}
public void request()
{
adaptee.specificRequest();
}
}
//客户端代码
public class ObjectAdapterTest
{
public static void main(String[] args)
{
System.out.println("对象适配器模式测试:");
Adaptee adaptee = new Adaptee();
Target target = new ObjectAdapter(adaptee);
target.request();
}
}
说明:对象适配器模式中的“目标接口”和“适配者类”的代码同类适配器模式一样,只要修改适配器类和客户端的代码即可。
8、桥接模式 (Bridge)
将抽象部分与它的实现部分分离,使它们都可以独立地变化。它是用组合关系代替继承关系来实现,从而降低了抽象和实现这两个可变维度的耦合度。
模式的优缺点:
优点:
抽象与实现分离,扩展能力强;
符合开闭原则;
符合合成复用原则;
其实现细节对客户透明。
缺点:
由于聚合关系建立在抽象层,要求开发者针对抽象化进行设计与编程,能正确地识别出系统中两个独立变化的维度,这增加了系统的理解与设计难度。
模式的结构:
桥接(Bridge)模式包含以下主要角色。
抽象化(Abstraction)角色:定义抽象类,并包含一个对实现化对象的引用;
扩展抽象化(Refined Abstraction)角色:是抽象化角色的子类,实现父类中的业务方法,并通过组合关系调用实现化角色中的业务方法;
实现化(Implementor)角色:定义实现化角色的接口,供扩展抽象化角色调用;
具体实现化(Concrete Implementor)角色:给出实现化角色接口的具体实现。
模式的实现:
package bridge;
public class BridgeTest {
public static void main(String[] args) {
Implementor imple = new ConcreteImplementorA();
Abstraction abs = new RefinedAbstraction(imple);
abs.Operation();
}
}
//实现化角色
interface Implementor {
public void OperationImpl();
}
//具体实现化角色
class ConcreteImplementorA implements Implementor {
public void OperationImpl() {
System.out.println("具体实现化(Concrete Implementor)角色被访问");
}
}
//抽象化角色
abstract class Abstraction {
protected Implementor imple;
protected Abstraction(Implementor imple) {
this.imple = imple;
}
public abstract void Operation();
}
//扩展抽象化角色
class RefinedAbstraction extends Abstraction {
protected RefinedAbstraction(Implementor imple) {
super(imple);
}
public void Operation() {
System.out.println("扩展抽象化(Refined Abstraction)角色被访问");
imple.OperationImpl();
}
}
9、装饰器模式 (Decorator)
指在不改变现有对象结构的情况下,动态地给该对象增加一些职责(即增加其额外功能)的模式。
模式的优缺点:
优点:
装饰器是继承的有力补充,比继承灵活,在不改变原有对象的情况下,动态的给一个对象扩展功能,即插即用;
通过使用不用装饰类及这些装饰类的排列组合,可以实现不同效果;
装饰器模式完全遵守开闭原则。
缺点是:装饰器模式会增加许多子类,过度使用会增加程序得复杂性。
模式的结构:
装饰器模式主要包含以下角色。
抽象构件(Component)角色:定义一个抽象接口以规范准备接收附加责任的对象;
具体构件(ConcreteComponent)角色:实现抽象构件,通过装饰角色为其添加一些职责;
抽象装饰(Decorator)角色:继承抽象构件,并包含具体构件的实例,可以通过其子类扩展具体构件的功能;
具体装饰(ConcreteDecorator)角色:实现抽象装饰的相关方法,并给具体构件对象添加附加的责任。
模式的实现:
package decorator;
public class DecoratorPattern {
public static void main(String[] args) {
Component p = new ConcreteComponent();
p.operation();
System.out.println("---------------------------------");
Component d = new ConcreteDecorator(p);
d.operation();
}
}
//抽象构件角色
interface Component {
public void operation();
}
//具体构件角色
class ConcreteComponent implements Component {
public ConcreteComponent() {
System.out.println("创建具体构件角色");
}
public void operation() {
System.out.println("调用具体构件角色的方法operation()");
}
}
//抽象装饰角色
class Decorator implements Component {
private Component component;
public Decorator(Component component) {
this.component = component;
}
public void operation() {
component.operation();
}
}
//具体装饰角色
class ConcreteDecorator extends Decorator {
public ConcreteDecorator(Component component) {
super(component);
}
public void operation() {
super.operation();
addedFunction();
}
public void addedFunction() {
System.out.println("为具体构件角色增加额外的功能addedFunction()");
}
}
10、外观模式 (Facade)
是一种通过为多个复杂的子系统提供一个一致的接口,而使这些子系统更加容易被访问的模式。该模式对外有一个统一接口,外部应用程序不用关心内部子系统的具体细节,这样会大大降低应用程序的复杂度,提高了程序的可维护性
模式的优缺点:
外观(Facade)模式是“迪米特法则”的典型应用,它有以下主要优点:
降低了子系统与客户端之间的耦合度,使得子系统的变化不会影响调用它的客户类;
对客户屏蔽了子系统组件,减少了客户处理的对象数目,并使得子系统使用起来更加容易;
降低了大型软件系统中的编译依赖性,简化了系统在不同平台之间的移植过程,因为编译一个子系统不会影响其他的子系统,也不会影响外观对象。
主要缺点如下:
不能很好地限制客户使用子系统类,很容易带来未知风险;
增加新的子系统可能需要修改外观类或客户端的源代码,违背了“开闭原则”。
模式的结构:
外观(Facade)模式的结构比较简单,主要是定义了一个高层接口。它包含了对各个子系统的引用,客户端可以通过它访问各个子系统的功能。
外观(Facade)模式包含以下主要角色。
外观(Facade)角色:为多个子系统对外提供一个共同的接口;
子系统(Sub System)角色:实现系统的部分功能,客户可以通过外观角色访问它;
客户(Client)角色:通过一个外观角色访问各个子系统的功能。
模式的实现:
package facade;
public class FacadePattern {
public static void main(String[] args) {
Facade f = new Facade();
f.method();
}
}
//外观角色
class Facade {
private SubSystem01 obj1 = new SubSystem01();
private SubSystem02 obj2 = new SubSystem02();
private SubSystem03 obj3 = new SubSystem03();
public void method() {
obj1.method1();
obj2.method2();
obj3.method3();
}
}
//子系统角色
class SubSystem01 {
public void method1() {
System.out.println("子系统01的method1()被调用!");
}
}
//子系统角色
class SubSystem02 {
public void method2() {
System.out.println("子系统02的method2()被调用!");
}
}
//子系统角色
class SubSystem03 {
public void method3() {
System.out.println("子系统03的method3()被调用!");
}
}
11、享元模式 (Flyweight)
运用共享技术来有效地支持大量细粒度对象的复用。它通过共享已经存在的对象来大幅度减少需要创建的对象数量、避免大量相似类的开销,从而提高系统资源的利用率。
模式的优缺点:
优点是:
相同对象只要保存一份,这降低了系统中对象的数量,从而降低了系统中细粒度对象给内存带来的压力。
缺点是:
为了使对象可以共享,需要将一些不能共享的状态外部化,这将增加程序的复杂性;
读取享元模式的外部状态会使得运行时间稍微变长。
模式的结构:
享元模式的定义提出了两个要求,细粒度和共享对象。因为要求细粒度,所以不可避免地会使对象数量多且性质相近,此时我们就将这些对象的信息分为两个部分:内部状态和外部状态。
内部状态指对象共享出来的信息,存储在享元信息内部,并且不回随环境的改变而改变;
外部状态指对象得以依赖的一个标记,随环境的改变而改变,不可共享。
享元模式的本质是缓存共享对象,降低内存消耗
享元模式的主要角色有如下:
抽象享元角色(Flyweight):是所有的具体享元类的基类,为具体享元规范需要实现的公共接口,非享元的外部状态以参数的形式通过方法传入;
具体享元(Concrete Flyweight)角色:实现抽象享元角色中所规定的接口;
非享元(Unsharable Flyweight)角色:是不可以共享的外部状态,它以参数的形式注入具体享元的相关方法中;
享元工厂(Flyweight Factory)角色:负责创建和管理享元角色。当客户对象请求一个享元对象时,享元工厂检査系统中是否存在符合要求的享元对象,如果存在则提供给客户;如果不存在的话,则创建一个新的享元对象。
模式的实现:
public class FlyweightPattern {
public static void main(String[] args) {
FlyweightFactory factory = new FlyweightFactory();
Flyweight f01 = factory.getFlyweight("a");
Flyweight f02 = factory.getFlyweight("a");
Flyweight f03 = factory.getFlyweight("a");
Flyweight f11 = factory.getFlyweight("b");
Flyweight f12 = factory.getFlyweight("b");
f01.operation(new UnsharedConcreteFlyweight("第1次调用a。"));
f02.operation(new UnsharedConcreteFlyweight("第2次调用a。"));
f03.operation(new UnsharedConcreteFlyweight("第3次调用a。"));
f11.operation(new UnsharedConcreteFlyweight("第1次调用b。"));
f12.operation(new UnsharedConcreteFlyweight("第2次调用b。"));
}
}
//非享元角色
class UnsharedConcreteFlyweight {
private String info;
UnsharedConcreteFlyweight(String info) {
this.info = info;
}
public String getInfo() {
return info;
}
public void setInfo(String info) {
this.info = info;
}
}
//抽象享元角色
interface Flyweight {
public void operation(UnsharedConcreteFlyweight state);
}
//具体享元角色
class ConcreteFlyweight implements Flyweight {
private String key;
ConcreteFlyweight(String key) {
this.key = key;
System.out.println("具体享元" + key + "被创建!");
}
public void operation(UnsharedConcreteFlyweight outState) {
System.out.print("具体享元" + key + "被调用,");
System.out.println("非享元信息是:" + outState.getInfo());
}
}
//享元工厂角色
class FlyweightFactory {
private HashMap<String, Flyweight> flyweights = new HashMap<String, Flyweight>();
public Flyweight getFlyweight(String key) {
Flyweight flyweight = (Flyweight) flyweights.get(key);
if (flyweight != null) {
System.out.println("具体享元" + key + "已经存在,被成功获取!");
} else {
flyweight = new ConcreteFlyweight(key);
flyweights.put(key, flyweight);
}
return flyweight;
}
}
12、组合模式 (Composite)
将对象组合成树形结构以表示“部分-整体”的层次结构。它使得客户对单个对象和复合对象的使用具有一致性。
模式的优缺点:
优点有:
组合模式使得客户端代码可以一致地处理单个对象和组合对象,无须关心自己处理的是单个对象,还是组合对象,这简化了客户端代码;
更容易在组合体内加入新的对象,客户端不会因为加入了新的对象而更改源代码,满足“开闭原则”;
缺点是:
设计较复杂,客户端需要花更多时间理清类之间的层次关系;
不容易限制容器中的构件;
不容易用继承的方法来增加构件的新功能;
模式的结构:
组合模式包含以下主要角色:
抽象构件(Component)角色:它的主要作用是为树叶构件和树枝构件声明公共接口,并实现它们的默认行为。在透明式的组合模式中抽象构件还声明访问和管理子类的接口;在安全式的组合模式中不声明访问和管理子类的接口,管理工作由树枝构件完成。(总的抽象类或接口,定义一些通用的方法,比如新增、删除);
树叶构件(Leaf)角色:是组合中的叶节点对象,它没有子节点,用于继承或实现抽象构件;
树枝构件(Composite)角色 / 中间构件:是组合中的分支节点对象,它有子节点,用于继承和实现抽象构件。它的主要作用是存储和管理子部件,通常包含 Add()、Remove()、GetChild() 等方法。
组合模式分为透明式的组合模式和安全式的组合模式。
模式的实现:
透明组合模式
public class CompositePattern {
public static void main(String[] args) {
Component c0 = new Composite();
Component c1 = new Composite();
Component leaf1 = new Leaf("1");
Component leaf2 = new Leaf("2");
Component leaf3 = new Leaf("3");
c0.add(leaf1);
c0.add(c1);
c1.add(leaf2);
c1.add(leaf3);
c0.operation();
}
}
//抽象构件
interface Component {
public void add(Component c);
public void remove(Component c);
public Component getChild(int i);
public void operation();
}
//树叶构件
class Leaf implements Component {
private String name;
public Leaf(String name) {
this.name = name;
}
public void add(Component c) {
}
public void remove(Component c) {
}
public Component getChild(int i) {
return null;
}
public void operation() {
System.out.println("树叶" + name + ":被访问!");
}
}
//树枝构件
class Composite implements Component {
private ArrayList<Component> children = new ArrayList<Component>();
public void add(Component c) {
children.add(c);
}
public void remove(Component c) {
children.remove(c);
}
public Component getChild(int i) {
return children.get(i);
}
public void operation() {
for (Object obj : children) {
((Component) obj).operation();
}
}
}
安全组合模式
安全式的组合模式与透明式组合模式的实现代码类似,只要对其做简单修改就可以了,代码如下:
首先修改 Component 代码,只保留层次的公共行为。
interface Component {
public void operation();
}
然后修改客户端代码,将树枝构件类型更改为 Composite 类型,以便获取管理子类操作的方法。
public class CompositePattern {
public static void main(String[] args) {
Composite c0 = new Composite();
Composite c1 = new Composite();
Component leaf1 = new Leaf("1");
Component leaf2 = new Leaf("2");
Component leaf3 = new Leaf("3");
c0.add(leaf1);
c0.add(c1);
c1.add(leaf2);
c1.add(leaf3);
c0.operation();
}
}
13、模板方法模式 (Template Method)
定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。Template Method使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
模式的优缺点:
优点:
它封装了不变部分,扩展可变部分。它把认为是不变部分的算法封装到父类中实现,而把可变部分算法由子类继承实现,便于子类继续扩展。
它在父类中提取了公共的部分代码,便于代码复用。
部分方法是由子类实现,因此子类可以通过扩展方式增加相应的功能,符合开闭原则。
缺点:
对于每个不同的实现都需要定义一个子类,这会导致类的个数增加,系统更加庞大,谁也更加抽象,间接地增加了系统实现的复杂度。
父类中的抽象方法由子类实现,子类执行的结构会影响父类的结果,这导致一种反向的控制结构,它提高了代码阅读的难度。
由于继承关系自身的缺点,如果父类添加新的抽象方法,则所有子类都要改一遍。
模式的结构:
模板方法模式需要注意抽象类与具体子类之间的协作。它用到了虚函数的多态性技术以及“不用调用我,让我来调用你”的反向控制技术。
模板方法模式包含以下主要角色:
1)抽象类/抽象模板(Abstract Class)
抽象模板类,负责给出一个算法的轮廓和骨架。它由一个模板方法和若干个基本方法构成。这些方法的定义如下:
模板方法:定义了算法的骨架,按某种顺序调用其包含的基本方法;
基本方法:是整个算法中的一个步骤,包含以下几种类型:
抽象方法:在抽象类中声明,由具体子类实现;
具体方法:在抽象类中已经实现,在具体子类中可以继承或重写它;
钩子方法:在抽象类中已经实现,包括用于判断的逻辑方法和需要子类重写的空方法两种
2)具体子类/具体实现(Concrete Class)
具体实现类,实现抽象类中所定义的抽象方法和钩子方法,它们是一个顶级逻辑的一个组成步骤。
模式的实现:
public class TemplateMethodPattern {
public static void main(String[] args) {
AbstractClass tm = new ConcreteClass();
tm.TemplateMethod();
}
}
//抽象类
abstract class AbstractClass {
//模板方法
public void TemplateMethod() {
SpecificMethod();
abstractMethod1();
abstractMethod2();
}
//具体方法
public void SpecificMethod() {
System.out.println("抽象类中的具体方法被调用...");
}
//抽象方法1
public abstract void abstractMethod1();
//抽象方法2
public abstract void abstractMethod2();
}
//具体子类
class ConcreteClass extends AbstractClass {
public void abstractMethod1() {
System.out.println("抽象方法1的实现被调用...");
}
public void abstractMethod2() {
System.out.println("抽象方法2的实现被调用...");
}
}
14、策略模式 (Strategy)
定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换,且算法的变化不会影响使用算法的客户。本模式使得算法的变化可独立于使用它的客户。
模式的优缺点:
主要优点如下:
多重条件语句不易维护,而使用策略模式可以避免使用多重条件语句,如 if...else 语句、switch...case 语句;
策略模式提供了一系列的可供重用的算法族,恰当使用继承可以把算法族的公共代码转移到父类里面,从而避免重复的代码;
策略模式可以提供相同行为的不同实现,客户可以根据不同时间或空间要求选择不同的;
策略模式提供了对开闭原则的完美支持,可以在不修改原代码的情况下,灵活增加新算法;
策略模式把算法的使用放到环境类中,而算法的实现移到具体策略类中,实现了二者的分离。
主要缺点如下:
客户端必须理解所有策略算法的区别,以便适时选择恰当的算法类;
策略模式造成很多的策略类,增加维护难度。
模式的结构:
策略模式是准备一组算法,并将这组算法封装到一系列的策略类里面,作为一个抽象策略类的子类。策略模式的重心不是如何实现算法,而是如何组织这些算法,从而让程序结构更加灵活,具有更好的维护性和扩展性,现在我们来分析其基本结构和实现方法。
策略模式的主要角色如下:
抽象策略(Strategy)类:定义了一个公共接口,各种不同的算法以不同的方式实现这个接口,环境角色使用这个接口调用不同的算法,一般使用接口或抽象类实现;
具体策略(Concrete Strategy)类:实现了抽象策略定义的接口,提供具体的算法实现;
环境(Context)类:持有一个策略类的引用,最终给客户端调用。
模式的实现:
public class StrategyPattern {
public static void main(String[] args) {
Context c = new Context();
Strategy s = new ConcreteStrategyA();
c.setStrategy(s);
c.strategyMethod();
System.out.println("-----------------");
s = new ConcreteStrategyB();
c.setStrategy(s);
c.strategyMethod();
}
}
//抽象策略类
interface Strategy {
public void strategyMethod(); //策略方法
}
//具体策略类A
class ConcreteStrategyA implements Strategy {
public void strategyMethod() {
System.out.println("具体策略A的策略方法被访问!");
}
}
//具体策略类B
class ConcreteStrategyB implements Strategy {
public void strategyMethod() {
System.out.println("具体策略B的策略方法被访问!");
}
}
//环境类
class Context {
private Strategy strategy;
public Strategy getStrategy() {
return strategy;
}
public void setStrategy(Strategy strategy) {
this.strategy = strategy;
}
public void strategyMethod() {
strategy.strategyMethod();
}
}
15、命令模式 (Command)
将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可取消的操作。这样两者之间通过命令对象进行沟通,这样方便将命令对象进行储存、传递、调用、增加与管理。
模式的优缺点:
主要优点如下:
通过引入中间件(抽象接口)降低系统的耦合度;
扩展性良好,增加或删除命令非常方便。采用命令模式增加与删除命令不会影响其他类,且满足“开闭原则”;
可以实现宏命令。命令模式可以与组合模式结合,将多个命令装配成一个组合命令,即宏命令;
方便实现 Undo 和 Redo 操作.命令模式可以与后面介绍的备忘录模式结合,实现命令的撤销与恢复;
可以在现有命令的基础上,增加额外功能。比如日志记录,结合装饰器模式会更加灵活。
其缺点是:
可能产生大量具体的命令类。因为每一个具体操作都需要设计一个具体命令类,这会增加系统的复杂性;
命令模式的结果其实就是接收方的执行结果,但是为了以命令的形式进行架构、解耦请求与实现,引入了额外类型结构(引入了请求方与抽象命令接口),增加了理解上的困难。不过这也是设计模式的通病,抽象必然会额外增加类的数量,代码抽离肯定比代码聚合更加难理解。
模式的结构:
可以将系统中的相关操作抽象成命令,使调用者与实现者相关分离。
命令模式包含以下主要角色:
抽象命令类(Command)角色:声明执行命令的接口,拥有执行命令的抽象方法 execute();
具体命令类(Concrete Command)角色:是抽象命令类的具体实现类,它拥有接收者对象,并通过调用接收者的功能来完成命令要执行的操作;
实现者/接收者(Receiver)角色:执行命令功能的相关操作,是具体命令对象业务的真正实现者;
调用者/请求者(Invoker)角色:是请求的发送者,它通常拥有很多的命令对象,并通过访问命令对象来执行相关请求,它不直接访问接收者。
模式的实现:
package command;
public class CommandPattern {
public static void main(String[] args) {
Command cmd = new ConcreteCommand();
Invoker ir = new Invoker(cmd);
System.out.println("客户访问调用者的call()方法...");
ir.call();
}
}
//调用者
class Invoker {
private Command command;
public Invoker(Command command) {
this.command = command;
}
public void setCommand(Command command) {
this.command = command;
}
public void call() {
System.out.println("调用者执行命令command...");
command.execute();
}
}
//抽象命令
interface Command {
public abstract void execute();
}
//具体命令
class ConcreteCommand implements Command {
private Receiver receiver;
ConcreteCommand() {
receiver = new Receiver();
}
public void execute() {
receiver.action();
}
}
//接收者
class Receiver {
public void action() {
System.out.println("接收者的action()方法被调用...");
}
}
16、责任链模式 (Chain of Responsibility)
为解除请求的发送者和接收者之间耦合,而使多个对象都有机会处理这个请求。将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它。
模式的优缺点:
优点:
降低了对象之间的耦合度。该模式使得一个对象无须知道到底是哪一个对象处理其请求以及链的结构,发送者和接收者也无须拥有对方的明确信息;
增强了系统的可扩展性。可以根据需要增加新的请求处理类,满足开闭原则;
增强了给对象指派职责的灵活性。当工作流程发生变化,可以动态地改变链内的成员或者调动它们的次序,也可动态地新增或者删除责任;
责任链简化了对象之间的连接。每个对象只需保持一个指向其后继者的引用,不需保持其他所有处理者的引用,这避免了使用众多的 if 或者 if···else 语句;
责任分担。每个类只需要处理自己该处理的工作,不该处理的传递给下一个对象完成,明确各类的责任范围,符合类的单一职责原则。
缺点:
不能保证每个请求一定被处理。由于一个请求没有明确的接收者,所以不能保证它一定会被处理,该请求可能一直传到链的末端都得不到处理;
对比较长的职责链,请求的处理可能涉及多个处理对象,系统性能将受到一定影响;
职责链建立的合理性要靠客户端来保证,增加了客户端的复杂性,可能会由于职责链的错误设置而导致系统出错,如可能会造成循环调用。
模式的结构:
职责链模式主要包含以下角色。
抽象处理者(Handler)角色:定义一个处理请求的接口,包含抽象处理方法和一个后继连接;
具体处理者(Concrete Handler)角色:实现抽象处理者的处理方法,判断能否处理本次请求,如果可以处理请求则处理,否则将该请求转给它的后继者;
客户类(Client)角色:创建处理链,并向链头的具体处理者对象提交请求,它不关心处理细节和请求的传递过程。
责任链模式的本质是解耦请求与处理,让请求在处理链中能进行传递与被处理;理解责任链模式应当理解其模式,而不是其具体实现。责任链模式的独到之处是将其节点处理者组合成了链式结构,并允许节点自身决定是否进行请求处理或转发,相当于让请求流动起来.
模式的实现:
package chainOfResponsibility;
public class ChainOfResponsibilityPattern {
public static void main(String[] args) {
//组装责任链
Handler handler1 = new ConcreteHandler1();
Handler handler2 = new ConcreteHandler2();
handler1.setNext(handler2);
//提交请求
handler1.handleRequest("two");
}
}
//抽象处理者角色
abstract class Handler {
private Handler next;
public void setNext(Handler next) {
this.next = next;
}
public Handler getNext() {
return next;
}
//处理请求的方法
public abstract void handleRequest(String request);
}
//具体处理者角色1
class ConcreteHandler1 extends Handler {
public void handleRequest(String request) {
if (request.equals("one")) {
System.out.println("具体处理者1负责处理该请求!");
} else {
if (getNext() != null) {
getNext().handleRequest(request);
} else {
System.out.println("没有人处理该请求!");
}
}
}
}
//具体处理者角色2
class ConcreteHandler2 extends Handler {
public void handleRequest(String request) {
if (request.equals("two")) {
System.out.println("具体处理者2负责处理该请求!");
} else {
if (getNext() != null) {
getNext().handleRequest(request);
} else {
System.out.println("没有人处理该请求!");
}
}
}
}
17、状态模式 (State)
对有状态的对象,把复杂的“判断逻辑”提取到不同的状态对象中,允许状态对象在其内部状态发生改变时改变其行为。
模式的优缺点:
其主要优点如下:
结构清晰,状态模式将与特定状态相关的行为局部化到一个状态中,并且将不同状态的行为分割开来,满足“单一职责原则”;
将状态转换显示化,减少对象间的相互依赖。将不同的状态引入独立的对象中会使得状态转换变得更加明确,且减少对象间的相互依赖;
状态类职责明确,有利于程序的扩展。通过定义新的子类很容易地增加新的状态和转换。
主要缺点如下:
状态模式的使用必然会增加系统的类与对象的个数;
状态模式的结构与实现都较为复杂,如果使用不当会导致程序结构和代码的混乱;
状态模式对开闭原则的支持并不太好,对于可以切换状态的状态模式,增加新的状态类需要修改那些负责状态转换的源码,否则无法切换到新增状态,而且修改某个状态类的行为也需要修改对应类的源码。
模式的结构:
状态模式把受环境改变的对象行为包装在不同的状态对象里,其意图是让一个对象在其内部状态改变的时候,其行为也随之改变。
状态模式包含以下主要角色:
环境类(Context)角色:也称为上下文,它定义了客户端需要的接口,内部维护一个当前状态,并负责具体状态的切换;
抽象状态(State)角色:定义一个接口,用以封装环境对象中的特定状态所对应的行为,可以有一个或多个行为;
具体状态(Concrete State)角色:实现抽象状态所对应的行为,并且在需要的情况下进行状态切换。
模式的实现:
public class StatePatternClient {
public static void main(String[] args) {
Context context = new Context(); //创建环境
context.Handle(); //处理请求
context.Handle();
context.Handle();
context.Handle();
}
}
//环境类
class Context {
private State state;
//定义环境类的初始状态
public Context() {
this.state = new ConcreteStateA();
}
//设置新状态
public void setState(State state) {
this.state = state;
}
//读取状态
public State getState() {
return (state);
}
//对请求做处理
public void Handle() {
state.Handle(this);
}
}
//抽象状态类
abstract class State {
public abstract void Handle(Context context);
}
//具体状态A类
class ConcreteStateA extends State {
public void Handle(Context context) {
System.out.println("当前状态是 A.");
context.setState(new ConcreteStateB());
}
}
//具体状态B类
class ConcreteStateB extends State {
public void Handle(Context context) {
System.out.println("当前状态是 B.");
context.setState(new ConcreteStateA());
}
}
18、观察者模式 (Observer)
定义对象间的一种一对多的依赖关系,以便当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并自动刷新。
模式的优缺点:
优点如下:
降低了目标与观察者之间的耦合关系,两者之间是抽象耦合关系。符合依赖倒置原则;
目标与观察者之间建立了一套触发机制。
缺点如下:
目标与观察者之间的依赖关系并没有完全解除,而且有可能出现循环引用;
当观察者对象很多时,通知的发布会花费很多时间,影响程序的效率。
模式的结构:
实现观察者模式时要注意具体目标对象和具体观察者对象之间不能直接调用,否则将使两者之间紧密耦合起来,这违反了面向对象的设计原则。
观察者模式的主要角色如下:
抽象主题(Subject)角色:也叫抽象目标类,它提供了一个用于保存观察者对象的聚集类和增加、删除观察者对象的方法,以及通知所有观察者的抽象方法;
具体主题(Concrete Subject)角色:也叫具体目标类,它实现抽象目标中的通知方法,当具体主题的内部状态发生改变时,通知所有注册过的观察者对象;
抽象观察者(Observer)角色:它是一个抽象类或接口,它包含了一个更新自己的抽象方法,当接到具体主题的更改通知时被调用;
具体观察者(Concrete Observer)角色:实现抽象观察者中定义的抽象方法,以便在得到目标的更改通知时更新自身的状态。
模式的实现:
package net.biancheng.c.observer;
import java.util.*;
public class ObserverPattern {
public static void main(String[] args) {
Subject subject = new ConcreteSubject();
Observer obs1 = new ConcreteObserver1();
Observer obs2 = new ConcreteObserver2();
subject.add(obs1);
subject.add(obs2);
subject.notifyObserver();
}
}
//抽象目标
abstract class Subject {
protected List<Observer> observers = new ArrayList<Observer>();
//增加观察者方法
public void add(Observer observer) {
observers.add(observer);
}
//删除观察者方法
public void remove(Observer observer) {
observers.remove(observer);
}
public abstract void notifyObserver(); //通知观察者方法
}
//具体目标
class ConcreteSubject extends Subject {
public void notifyObserver() {
System.out.println("具体目标发生改变...");
System.out.println("--------------");
for (Object obs : observers) {
((Observer) obs).response();
}
}
}
//抽象观察者
interface Observer {
void response(); //反应
}
//具体观察者1
class ConcreteObserver1 implements Observer {
public void response() {
System.out.println("具体观察者1作出反应!");
}
}
//具体观察者1
class ConcreteObserver2 implements Observer {
public void response() {
System.out.println("具体观察者2作出反应!");
}
}
19、中介者模式 (Mediator)
定义一个中介对象来封装一系列对象之间的交互,使原有对象之间的耦合松散,且可以独立地改变它们之间的交互。中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。
模式的优缺点:
优点如下:
类之间各司其职,符合迪米特法则;
降低了对象之间的耦合性,使得对象易于独立地被复用;
将对象间的一对多关联转变为一对一的关联,提高系统的灵活性,使得系统易于维护和扩展。
缺点是:
中介者模式将原本多个对象直接的相互依赖变成了中介者和多个同事类的依赖关系。当同事类越多时,中介者就会越臃肿,变得复杂且难以维护。
模式的结构:
中介者模式包含以下主要角色:
抽象中介者(Mediator)角色:它是中介者的接口,提供了同事对象注册与转发同事对象信息的抽象方法;
具体中介者(Concrete Mediator)角色:实现中介者接口,定义一个 List 来管理同事对象,协调各个同事角色之间的交互关系,因此它依赖于同事角色;
抽象同事类(Colleague)角色:定义同事类的接口,保存中介者对象,提供同事对象交互的抽象方法,实现所有相互影响的同事类的公共功能;
具体同事类(Concrete Colleague)角色:是抽象同事类的实现者,当需要与其他同事对象交互时,由中介者对象负责后续的交互。
模式的实现:
package net.biancheng.c.mediator;
import java.util.*;
public class MediatorPattern {
public static void main(String[] args) {
Mediator md = new ConcreteMediator();
Colleague c1, c2;
c1 = new ConcreteColleague1();
c2 = new ConcreteColleague2();
md.register(c1);
md.register(c2);
c1.send();
System.out.println("-------------");
c2.send();
}
}
//抽象中介者
abstract class Mediator {
public abstract void register(Colleague colleague);
public abstract void relay(Colleague cl); //转发
}
//具体中介者
class ConcreteMediator extends Mediator {
private List<Colleague> colleagues = new ArrayList<Colleague>();
public void register(Colleague colleague) {
if (!colleagues.contains(colleague)) {
colleagues.add(colleague);
colleague.setMedium(this);
}
}
public void relay(Colleague cl) {
for (Colleague ob : colleagues) {
if (!ob.equals(cl)) {
((Colleague) ob).receive();
}
}
}
}
//抽象同事类
abstract class Colleague {
protected Mediator mediator;
public void setMedium(Mediator mediator) {
this.mediator = mediator;
}
public abstract void receive();
public abstract void send();
}
//具体同事类
class ConcreteColleague1 extends Colleague {
public void receive() {
System.out.println("具体同事类1收到请求。");
}
public void send() {
System.out.println("具体同事类1发出请求。");
mediator.relay(this); //请中介者转发
}
}
//具体同事类
class ConcreteColleague2 extends Colleague {
public void receive() {
System.out.println("具体同事类2收到请求。");
}
public void send() {
System.out.println("具体同事类2发出请求。");
mediator.relay(this); //请中介者转发
}
}
20、迭代器模式 (Iterator)
提供一种方法顺序访问一个聚合对象中各个元素, 而又不需暴露该对象的内部表示。
模式的优缺点:
优点如下:
访问一个聚合对象的内容而无须暴露它的内部表示;
遍历任务交由迭代器完成,这简化了聚合类;
它支持以不同方式遍历一个聚合,甚至可以自定义迭代器的子类以支持新的遍历;
增加新的聚合类和迭代器类都很方便,无须修改原有代码;
封装性良好,为遍历不同的聚合结构提供一个统一的接口。
其主要缺点是:增加了类的个数,这在一定程度上增加了系统的复杂性。
模式的结构:
迭代器模式是通过将聚合对象的遍历行为分离出来,抽象成迭代器类来实现的,其目的是在不暴露聚合对象的内部结构的情况下,让外部代码透明地访问聚合的内部数据。
迭代器模式主要包含以下角色:
抽象聚合(Aggregate)角色:定义存储、添加、删除聚合对象以及创建迭代器对象的接口;
具体聚合(ConcreteAggregate)角色:实现抽象聚合类,返回一个具体迭代器的实例;
抽象迭代器(Iterator)角色:定义访问和遍历聚合元素的接口,通常包含 hasNext()、first()、next() 等方法;
具体迭代器(Concretelterator)角色:实现抽象迭代器接口中所定义的方法,完成对聚合对象的遍历,记录遍历的当前位置。
模式的实现:
package net.biancheng.c.iterator;
import java.util.*;
public class IteratorPattern {
public static void main(String[] args) {
Aggregate ag = new ConcreteAggregate();
ag.add("中山大学");
ag.add("华南理工");
ag.add("韶关学院");
System.out.print("聚合的内容有:");
Iterator it = ag.getIterator();
while (it.hasNext()) {
Object ob = it.next();
System.out.print(ob.toString() + "\t");
}
Object ob = it.first();
System.out.println("\nFirst:" + ob.toString());
}
}
//抽象聚合
interface Aggregate {
public void add(Object obj);
public void remove(Object obj);
public Iterator getIterator();
}
//具体聚合
class ConcreteAggregate implements Aggregate {
private List<Object> list = new ArrayList<Object>();
public void add(Object obj) {
list.add(obj);
}
public void remove(Object obj) {
list.remove(obj);
}
public Iterator getIterator() {
return (new ConcreteIterator(list));
}
}
//抽象迭代器
interface Iterator {
Object first();
Object next();
boolean hasNext();
}
//具体迭代器
class ConcreteIterator implements Iterator {
private List<Object> list = null;
private int index = -1;
public ConcreteIterator(List<Object> list) {
this.list = list;
}
public boolean hasNext() {
if (index < list.size() - 1) {
return true;
} else {
return false;
}
}
public Object first() {
index = 0;
Object obj = list.get(index);
;
return obj;
}
public Object next() {
Object obj = null;
if (this.hasNext()) {
obj = list.get(++index);
}
return obj;
}
}
21、访问者模式 (Visitor)
将作用于某种数据结构中的各元素的操作分离出来封装成独立的类,使其在不改变数据结构的前提下可以添加作用于这些元素的新的操作,为数据结构中的每个元素提供多种访问方式。
模式的优缺点:
优点如下:
扩展性好。能够在不修改对象结构中的元素的情况下,为对象结构中的元素添加新的功能;
复用性好。可以通过访问者来定义整个对象结构通用的功能,从而提高系统的复用程度;
灵活性好。访问者模式将数据结构与作用于结构上的操作解耦,使得操作集合可相对自由地演化而不影响系统的数据结构;
符合单一职责原则。访问者模式把相关的行为封装在一起,构成一个访问者,使每一个访问者的功能都比较单一。
主要缺点如下:
增加新的元素类很困难。在访问者模式中,每增加一个新的元素类,都要在每一个具体访问者类中增加相应的具体操作,这违背了“开闭原则”;
破坏封装。访问者模式中具体元素对访问者公布细节,这破坏了对象的封装性;
违反了依赖倒置原则。访问者模式依赖了具体类,而没有依赖抽象类。
模式的结构:
访问者模式包含以下主要角色:
抽象访问者(Visitor)角色:定义一个访问具体元素的接口,为每个具体元素类对应一个访问操作 visit() ,该操作中的参数类型标识了被访问的具体元素;
具体访问者(ConcreteVisitor)角色:实现抽象访问者角色中声明的各个访问操作,确定访问者访问一个元素时该做什么;
抽象元素(Element)角色:声明一个包含接受操作 accept() 的接口,被接受的访问者对象作为 accept() 方法的参数;
具体元素(ConcreteElement)角色:实现抽象元素角色提供的 accept() 操作,其方法体通常都是 visitor.visit(this) ,另外具体元素中可能还包含本身业务逻辑的相关操作;
对象结构(Object Structure)角色:是一个包含元素角色的容器,提供让访问者对象遍历容器中的所有元素的方法,通常由 List、Set、Map 等聚合类实现。
模式的实现:
package net.biancheng.c.visitor;
import java.util.*;
public class VisitorPattern {
public static void main(String[] args) {
ObjectStructure os = new ObjectStructure();
os.add(new ConcreteElementA());
os.add(new ConcreteElementB());
Visitor visitor = new ConcreteVisitorA();
os.accept(visitor);
System.out.println("------------------------");
visitor = new ConcreteVisitorB();
os.accept(visitor);
}
}
//抽象访问者
interface Visitor {
void visit(ConcreteElementA element);
void visit(ConcreteElementB element);
}
//具体访问者A类
class ConcreteVisitorA implements Visitor {
public void visit(ConcreteElementA element) {
System.out.println("具体访问者A访问-->" + element.operationA());
}
public void visit(ConcreteElementB element) {
System.out.println("具体访问者A访问-->" + element.operationB());
}
}
//具体访问者B类
class ConcreteVisitorB implements Visitor {
public void visit(ConcreteElementA element) {
System.out.println("具体访问者B访问-->" + element.operationA());
}
public void visit(ConcreteElementB element) {
System.out.println("具体访问者B访问-->" + element.operationB());
}
}
//抽象元素类
interface Element {
void accept(Visitor visitor);
}
//具体元素A类
class ConcreteElementA implements Element {
public void accept(Visitor visitor) {
visitor.visit(this);
}
public String operationA() {
return "具体元素A的操作。";
}
}
//具体元素B类
class ConcreteElementB implements Element {
public void accept(Visitor visitor) {
visitor.visit(this);
}
public String operationB() {
return "具体元素B的操作。";
}
}
//对象结构角色
class ObjectStructure {
private List<Element> list = new ArrayList<Element>();
public void accept(Visitor visitor) {
Iterator<Element> i = list.iterator();
while (i.hasNext()) {
((Element) i.next()).accept(visitor);
}
}
public void add(Element element) {
list.add(element);
}
public void remove(Element element) {
list.remove(element);
}
}
22、备忘录模式 (Memento)
在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到保存的状态。
模式的优缺点:
主要优点如下:
提供了一种可以恢复状态的机制。当用户需要时能够比较方便地将数据恢复到某个历史的状态;
实现了内部状态的封装。除了创建它的发起人之外,其他对象都不能够访问这些状态信息;
简化了发起人类。发起人不需要管理和保存其内部状态的各个备份,所有状态信息都保存在备忘录中,并由管理者进行管理,这符合单一职责原则。
其主要缺点是:资源消耗大。如果要保存的内部状态信息过多或者特别频繁,将会占用比较大的内存资源。
模式的结构:
备忘录模式的主要角色如下:
发起人(Originator)角色:记录当前时刻的内部状态信息,提供创建备忘录和恢复备忘录数据的功能,实现其他业务功能,它可以访问备忘录里的所有信息;
备忘录(Memento)角色:负责存储发起人的内部状态,在需要的时候提供这些内部状态给发起人;
管理者(Caretaker)角色:对备忘录进行管理,提供保存与获取备忘录的功能,但其不能对备忘录的内容进行访问与修改。
模式的实现:
package net.biancheng.c.memento;
public class MementoPattern {
public static void main(String[] args) {
Originator or = new Originator();
Caretaker cr = new Caretaker();
or.setState("S0");
System.out.println("初始状态:" + or.getState());
cr.setMemento(or.createMemento()); //保存状态
or.setState("S1");
System.out.println("新的状态:" + or.getState());
or.restoreMemento(cr.getMemento()); //恢复状态
System.out.println("恢复状态:" + or.getState());
}
}
//备忘录
class Memento {
private String state;
public Memento(String state) {
this.state = state;
}
public void setState(String state) {
this.state = state;
}
public String getState() {
return state;
}
}
//发起人
class Originator {
private String state;
public void setState(String state) {
this.state = state;
}
public String getState() {
return state;
}
public Memento createMemento() {
return new Memento(state);
}
public void restoreMemento(Memento m) {
this.setState(m.getState());
}
}
//管理者
class Caretaker {
private Memento memento;
public void setMemento(Memento m) {
memento = m;
}
public Memento getMemento() {
return memento;
}
}
23、解释器模式 (Interpreter)
给定一个语言, 定义它的文法的一种表示,并定义一个解释器, 该解释器使用该表示来解释语言中的句子。也就是说,用编译语言的方式来分析应用中的实例。
模式的优缺点:
主要优点如下:
扩展性好。由于在解释器模式中使用类来表示语言的文法规则,因此可以通过继承等机制来改变或扩展文法;
容易实现。在语法树中的每个表达式节点类都是相似的,所以实现其文法较为容易。
缺点如下:
执行效率较低。解释器模式中通常使用大量的循环和递归调用,当要解释的句子较复杂时,其运行速度很慢,且代码的调试过程也比较麻烦;
会引起类膨胀。解释器模式中的每条规则至少需要定义一个类,当包含的文法规则很多时,类的个数将急剧增加,导致系统难以管理与维护;
可应用的场景比较少。在软件开发中,需要定义语言文法的应用实例非常少,所以这种模式很少被使用到。
模式的结构:
解释器模式包含以下主要角色:
抽象表达式(Abstract Expression)角色:定义解释器的接口,约定解释器的解释操作,主要包含解释方法 interpret();
终结符表达式(Terminal Expression)角色:是抽象表达式的子类,用来实现文法中与终结符相关的操作,文法中的每一个终结符都有一个具体终结表达式与之相对应;
非终结符表达式(Nonterminal Expression)角色:也是抽象表达式的子类,用来实现文法中与非终结符相关的操作,文法中的每条规则都对应于一个非终结符表达式;
环境(Context)角色:通常包含各个解释器需要的数据或是公共的功能,一般用来传递被所有解释器共享的数据,后面的解释器可以从这里获取这些值;
客户端(Client):主要任务是将需要分析的句子或表达式转换成使用解释器对象描述的抽象语法树,然后调用解释器的解释方法,当然也可以通过环境角色间接访问解释器的解释方法;
模式的实现:
解释器模式实现的关键是定义文法规则、设计终结符类与非终结符类、画出结构图,必要时构建语法树,其代码结构如下:
package net.biancheng.c.interpreter;
//抽象表达式类
interface AbstractExpression {
public void interpret(String info); //解释方法
}
//终结符表达式类
class TerminalExpression implements AbstractExpression {
public void interpret(String info) {
//对终结符表达式的处理
}
}
//非终结符表达式类
class NonterminalExpression implements AbstractExpression {
private AbstractExpression exp1;
private AbstractExpression exp2;
public void interpret(String info) {
//非对终结符表达式的处理
}
}
//环境类
class Context {
private AbstractExpression exp;
public Context() {
//数据初始化
}
public void operation(String info) {
//调用相关表达式类的解释方法
}
}
看到这里的你很厉害哦,顺手点个赞哦,谢谢~
————————————————
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/m0_61008247/article/details/120404880