设计模式

设计模式

持续更新ing…

23种设计模式的分类如下图所示
C:\Users\konka\AppData\Roaming\Typora\typora-user-images\image-20200225093218987.png

23种设计模式的功能

  1. 单例(Singleton)模式:某个类只能生成一个实例,该类提供了一个全局访问点供外部获取该实例,其拓展是有限多例模式。
  2. 原型(Prototype)模式:将一个对象作为原型,通过对其进行复制而克隆出多个和原型类似的新实例。
  3. 工厂方法(Factory Method)模式:定义一个用于创建产品的接口,由子类决定生产什么产品。
  4. 抽象工厂(AbstractFactory)模式:提供一个创建产品族的接口,其每个子类可以生产一系列相关的产品。
  5. 建造者(Builder)模式:将一个复杂对象分解成多个相对简单的部分,然后根据不同需要分别创建它们,最后构建成该复杂对象。
  6. 代理(Proxy)模式:为某对象提供一种代理以控制对该对象的访问。即客户端通过代理间接地访问该对象,从而限制、增强或修改该对象的一些特性。
  7. 适配器(Adapter)模式:将一个类的接口转换成客户希望的另外一个接口,使得原本由于接口不兼容而不能一起工作的那些类能一起工作。
  8. 桥接(Bridge)模式:将抽象与实现分离,使它们可以独立变化。它是用组合关系代替继承关系来实现,从而降低了抽象和实现这两个可变维度的耦合度。
  9. 装饰(Decorator)模式:动态的给对象增加一些职责,即增加其额外的功能。
  10. 外观(Facade)模式:为多个复杂的子系统提供一个一致的接口,使这些子系统更加容易被访问。
  11. 享元(Flyweight)模式:运用共享技术来有效地支持大量细粒度对象的复用。
  12. 组合(Composite)模式:将对象组合成树状层次结构,使用户对单个对象和组合对象具有一致的访问性。
  13. 模板方法(TemplateMethod)模式:定义一个操作中的算法骨架,而将算法的一些步骤延迟到子类中,使得子类可以不改变该算法结构的情况下重定义该算法的某些特定步骤。
  14. 策略(Strategy)模式:定义了一系列算法,并将每个算法封装起来,使它们可以相互替换,且算法的改变不会影响使用算法的客户。
  15. 命令(Command)模式:将一个请求封装为一个对象,使发出请求的责任和执行请求的责任分割开。
  16. 职责链(Chain of Responsibility)模式:把请求从链中的一个对象传到下一个对象,直到请求被响应为止。通过这种方式去除对象之间的耦合。
  17. 状态(State)模式:允许一个对象在其内部状态发生改变时改变其行为能力。
  18. 观察者(Observer)模式:多个对象间存在一对多关系,当一个对象发生改变时,把这种改变通知给其他多个对象,从而影响其他对象的行为。
  19. 中介者(Mediator)模式:定义一个中介对象来简化原有对象之间的交互关系,降低系统中对象间的耦合度,使原有对象之间不必相互了解。
  20. 迭代器(Iterator)模式:提供一种方法来顺序访问聚合对象中的一系列数据,而不暴露聚合对象的内部表示。
  21. 访问者(Visitor)模式:在不改变集合元素的前提下,为一个集合中的每个元素提供多种访问方式,即每个元素有多个访问者对象访问。
  22. 备忘录(Memento)模式:在不破坏封装性的前提下,获取并保存一个对象的内部状态,以便以后恢复它。
  23. 解释器(Interpreter)模式:提供如何定义语言的文法,以及对语言句子的解释方法,即解释器。

开闭原则

定义

当应用的需求改变时,在不修改软件实体的源代码或者二进制代码的前提下,可以扩展模块的功能,使其满足新的需求。

实现

“抽象约束、封装变化”来实现开闭原则,即通过接口或者抽象类为软件实体定义一个相对稳定的抽象层,而将相同的可变因素封装在相同的具体实现类中。

例如Windows的桌面设计

用户可以根据自己的喜爱更换自己的桌面主题,也可以从网上下载新的主题。这些主题有共同的特点,可以为其定义一个抽象类(Abstract Subject),而每个具体的主题(Specific Subject)是其子类。用户窗体可以根据需要选择或者增加新的主题,而不需要修改原代码

Windows的桌面主题类图

里氏替换原则

定义

什么时候应该使用继承,什么时候不应该使用继承。里氏替换原是对开闭原则的补充,是对实现抽象化的具体步骤的规范。

实现方法

子类可以扩展父类的功能,但不能改变父类原有的功能。

几维鸟不是鸟实例

新西兰的几维鸟由于翅膀退化无法飞行。假如要设计一个实例,计算这两种鸟飞行 300 千米要花费的时间。显然,拿燕子来测试这段代码,结果正确,能计算出所需要的时间;但拿几维鸟来测试,结果会发生“除零异常”或是“无穷大”,类图如下

“几维鸟不是鸟”实例的类图

正确的做法是:取消几维鸟原来的继承关系,定义鸟和几维鸟的更一般的父类,如动物类,它们都有奔跑的能力。几维鸟的飞行速度虽然为 0,但奔跑速度不为 0,可以计算出其奔跑 300 千米所要花费的时间。其类图如图

“几维鸟是动物”实例的类图

合成复用原则

定义

要求在软件复用时,要尽量先使用组合或者聚合等关联关系来实现,其次才考虑使用继承关系来实现。

实现

通过将已有的对象纳入新对象中,作为新对象的成员对象来实现的,新对象可以调用已有对象的功能,从而达到复用。

汽车分类管理示例

汽车按“动力源”划分可分为汽油汽车、电动汽车等;按“颜色”划分可分为白色汽车、黑色汽车和红色汽车等。如果同时考虑这两种分类,其组合就很多。

可以看出用继承关系实现会产生很多子类,而且增加新的“动力源”或者增加新的“颜色”都要修改源代码,这违背了开闭原则,显然不可取。

用继承关系实现的汽车分类的类图

改用组合关系实现就能很好地解决以上问题

用组合关系实现的汽车分类的类图

依赖倒置原则

定义

要面向接口编程,不要面向实现编程。高层模块不应该依赖低层模块,两者都应该依赖其抽象;抽象不应该依赖细节,细节应该依赖抽象。

实现

  1. 每个类尽量提供接口或抽象类,或者两者都具备。
  2. 变量的声明类型尽量是接口或者是抽象类。
  3. 任何类都不应该从具体类派生。
  4. 使用继承时尽量遵循里氏替换原则。

顾客购物程序示例

顾客类 Customer 访问什么商店,或者增加新的商店,都是由Shop接口处理,具体的商店实现Shop接口,提供不同的购买方法。

顾客购物程序的类图

public class DIPtest
{
    public static void main(String[] args)
    {
        Customer wang=new Customer();
        System.out.println("顾客购买以下商品:"); 
        wang.shopping(new ShaoguanShop()); 
        wang.shopping(new WuyuanShop());
    }
}
//商店
interface Shop
{
    public String sell(); //卖
}
//韶关网店
class ShaoguanShop implements Shop
{
    public String sell()
    {
        return "韶关土特产:香菇、木耳……"; 
    } 
}
//婺源网店
class WuyuanShop implements Shop
{
    public String sell()
    {
        return "婺源土特产:绿茶、酒糟鱼……"; 
    }
} 
//顾客
class Customer
{
    public void shopping(Shop shop)
    {
        //购物
        System.out.println(shop.sell()); 
    }
}

单一职责原则

定义

单一职责原则规定一个类应该有且仅有一个引起它变化的原因,否则类应该被拆分。

单一职责同样也适用于方法。一个方法应该尽可能做好一件事情。

实现

需要设计人员发现类的不同职责并将其分离,再封装到不同的类或模块中。

大学生工作管理示例

大学学生工作管理程序的类图

接口隔离原则

定义

要为各个类建立它们需要的专用接口,而不要试图去建立一个很庞大的接口供所有依赖它的类去调用。

接口隔离 VS 单一职责

  • 单一职责原则注重的是职责,而接口隔离原则注重的是对接口依赖的隔离。
  • 单一职责原则主要是约束类,它针对的是程序中的实现和细节;接口隔离原则主要约束接口,主要针对抽象和程序整体框架的构建。

学生成绩管理程序

学生成绩管理程序一般包含插入成绩、删除成绩、修改成绩、计算总分、计算均分、打印成绩信息、査询成绩信息等功能,正确的做法是将它们分别放在输入模块、统计模块和打印模块等 3 个模块中

学生成绩管理程序的类图

迪米特法则

定义

只与你的直接朋友交谈,不跟“陌生人”说话

如果两个软件实体无须直接通信,那么就不应当发生直接的相互调用,可以通过第三方转发该调用。

“朋友”:当前对象本身、当前对象的成员对象、当前对象所创建的对象、当前对象的方法参数等,这些对象同当前对象存在关联、聚合或组合关系,可以直接访问这些对象的方法。

实现方法

  1. 从依赖者的角度来说,只依赖应该依赖的对象。
  2. 从被依赖者的角度说,只暴露应该暴露的方法。

在运用迪米特法则时要注意以下 6 点。

  1. 在类的划分上,应该创建弱耦合的类。类与类之间的耦合越弱,就越有利于实现可复用的目标。
  2. 在类的结构设计上,尽量降低类成员的访问权限。
  3. 在类的设计上,优先考虑将一个类设置成不变类。
  4. 在对其他类的引用上,将引用其他对象的次数降到最低。
  5. 不暴露类的属性成员,而应该提供相应的访问器(set 和 get 方法)。
  6. 谨慎使用序列化(Serializable)功能。

明星与经纪人的关系实例

明星由于全身心投入艺术,所以许多日常事务由经纪人负责处理,如与粉丝的见面会,与媒体公司的业务洽淡等。这里的经纪人是明星的朋友,而粉丝和媒体公司是陌生人,所以适合使用迪米特法则

明星与经纪人的关系图

创建型模式

创建型模式的主要关注点是“怎样创建对象?”,它的主要特点是“将对象的创建与使用分离”。

创建型模式分为以下几种。

  • 单例(Singleton)模式:某个类只能生成一个实例,该类提供了一个全局访问点供外部获取该实例,其拓展是有限多例模式。
  • 原型(Prototype)模式:将一个对象作为原型,通过对其进行复制而克隆出多个和原型类似的新实例。
  • 工厂方法(FactoryMethod)模式:定义一个用于创建产品的接口,由子类决定生产什么产品。
  • 抽象工厂(AbstractFactory)模式:提供一个创建产品族的接口,其每个子类可以生产一系列相关的产品。
  • 建造者(Builder)模式:将一个复杂对象分解成多个相对简单的部分,然后根据不同需要分别创建它们,最后构建成该复杂对象。

单例模式

定义

  1. 单例类只有一个实例对象;
  2. 该单例对象必须由单例类自行创建;
  3. 单例类对外提供一个访问该单例的全局访问点。

应用场景

  • 在应用场景中,某类只要求生成一个对象的时候。
  • 当对象需要被共享的场合。由于单例模式只允许创建一个对象,共享该对象可以节省内存,并加快对象访问速度。如 Web 中的配置对象、数据库的连接池等。
  • 当某类需要频繁实例化,而创建的对象又频繁被销毁的时候,如多线程的线程池、网络连接池等。

饿汉模式

这种方式在类加载时完成初始化,因此类加载慢,没有达到懒加载的效果。如果从未使用这个实例,则会造成内存浪费。但获取对象速度快,避免多线程同步的问题。

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

懒汉模式(线程不安全)

懒汉模式声明了一个静态对象,在用户第一次调用时初始化。这虽然节约了资源,但第一次加载时需要实例化,反应稍慢一些,而且在多线程时不能正常工作。

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

懒汉模式(线程安全)

能够在多线程中很好地工作,但是每次调用getInstance方法时都需要进行同步。这会造成不必要的同步开销,而且大部分时候我们是用不到同步的。所以不建议用这种模式。

public class Singleton{
    private static Singleton instance;
    private Singleton(){
        
    }
    public static synchronized Singleton getInstance(){
        if(instance == null){
            instance = new Singleton();
        }
        return instance;
    }
}

双重检查模式(DCL)

DCL的优点是资源利用率高。第一次执行getInstance时单例对象才被实例化,效率高。
其缺点是第一次加载时反应稍慢一些,在高并发环境下也有一定的缺陷。DCL虽然在一定程度上解决了资源的消耗和多余的同步、线程安全等问题,但其还是在某些情况会出现失效的问题。

注意:无论修饰静态方法还是对象都是类锁,DCL和线程安全的懒汉模式使用的都是类锁。

区别在于DCL只有在第一次创建实例时才会同步,而懒汉模式每次调用getInstance都会同步。

public class Singleton{
    private volatile static Singleton singleton;
    private Singleton(){
        
    }
    public static Singleton getInstance(){
        if(instance == null){
            synchronized(Singleton.class){
                if(instance == null){
                    instance = new Singleton();
                }
            }
        }
        return singleton;
    }
    
}

静态内部类单例模式

第一次加载Singleton类时并不会初始化sInstance,只有第一次调用getInstance方法时虚拟机加载SingletonHolder 并初始化 sInstance。这样不仅能确保线程安全,也能保证 Singleton 类的唯一性。

public class Singleton{
    private Singleton(){
        
    }
    public static Singleton getInstance(){
        return SingletonHolder.sInstance;
    }
    private static class SingletonHolder{
        private static final Singleton sInstance = new Singleton();
    }
}

原型模式

定义

用一个已经创建的实例作为原型,通过复制该原型对象来创建一个和原型相同或相似的新对象。

模式结构

  1. 抽象原型类:规定了具体原型对象必须实现的接口。
  2. 具体原型类:实现抽象原型类的 clone() 方法,它是可被复制的对象。
  3. 访问类:使用具体原型类中的 clone() 方法来复制新的对象。

原型模式的结构图

应用场景

  • 对象之间相同或相似,即只是个别的几个属性不同的时候。
  • 对象的创建过程比较麻烦,但复制比较简单的时候。

实现示例

用带原型管理器的原型模式来生成包含“圆”和“正方形”等图形的原型,并计算其面积。分析:本实例中由于存在不同的图形类,例如,“圆”和“正方形”,它们计算面积的方法不一样,所以需要用一个原型管理器来管理它们。

带原型管理器的原型模式的结构图

带原型管理器的原型模式的结构图

图形生成器的结构图

图形生成器的结构图

import java.util.*;
interface Shape extends Cloneable
{
    public Object clone();    //拷贝
    public void countArea();    //计算面积
}
class Circle implements Shape
{
    public Object clone()
    {
        Circle w=null;
        try
        {
            w=(Circle)super.clone();
        }
        catch(CloneNotSupportedException e)
        {
            System.out.println("拷贝圆失败!");
        }
        return w;
    }
    public void countArea()
    {
        int r=0;
        System.out.print("这是一个圆,请输入圆的半径:");
        Scanner input=new Scanner(System.in);
        r=input.nextInt();
        System.out.println("该圆的面积="+3.1415*r*r+"\n");
    }
}
class Square implements Shape
{
    public Object clone()
    {
        Square b=null;
        try
        {
            b=(Square)super.clone();
        }
        catch(CloneNotSupportedException e)
        {
            System.out.println("拷贝正方形失败!");
        }
        return b;
    }
    public void countArea()
    {
        int a=0;
        System.out.print("这是一个正方形,请输入它的边长:");
        Scanner input=new Scanner(System.in);
        a=input.nextInt();
        System.out.println("该正方形的面积="+a*a+"\n");
    }
}
class ProtoTypeManager
{
    private HashMap<String, Shape>ht=new HashMap<String,Shape>(); 
    public ProtoTypeManager()
    {
        ht.put("Circle",new Circle());
           ht.put("Square",new Square());
    } 
    public void addshape(String key,Shape obj)
    {
        ht.put(key,obj);
    }
    public Shape getShape(String key)
    {
        Shape temp=ht.get(key);
        return (Shape) temp.clone();
    }
}
public class ProtoTypeShape
{
    public static void main(String[] args)
    {
        ProtoTypeManager pm=new ProtoTypeManager();    
        Shape obj1=(Circle)pm.getShape("Circle");
        obj1.countArea();          
        Shape obj2=(Shape)pm.getShape("Square");
        obj2.countArea();     
    }
}

工厂方法模式

定义

定义一个创建产品对象的工厂接口,将产品对象的实际创建工作推迟到具体子工厂类当中。这满足创建型模式中所要求的“创建与使用相分离”的特点。

模式结构

此处展示的是工厂模式的结构模式:

工厂方法模式的结构图

  1. 抽象工厂(Abstract Factory):提供了创建产品的接口,调用者通过它访问具体工厂的工厂方法 newProduct() 来创建产品。
  2. 具体工厂(ConcreteFactory):主要是实现抽象工厂中的抽象方法,完成具体产品的创建。
  3. 抽象产品(Product):定义了产品的规范,描述了产品的主要特性和功能。
  4. 具体产品(ConcreteProduct):实现了抽象产品角色所定义的接口,由具体工厂来创建,它同具体工厂之间一一对应。

简单工厂模式

当需要生成的产品不多且不会增加,一个具体工厂类就可以完成任务时,可删除抽象工厂类。

简单工厂模式的结构图

应用场景

  • 客户只知道创建产品的工厂名,而不知道具体的产品名。如 TCL 电视工厂、海信电视工厂等。
  • 创建对象的任务由多个具体子工厂中的某一个完成,而抽象工厂只提供创建产品的接口。
  • 客户不关心创建产品的细节,只关心产品的品牌。

实现示例

用工厂方法模式设计畜牧场。有很多种类的畜牧场,如养马场用于养马,养牛场用于养牛,所以该实例用工厂方法模式比较适合。

畜牧场结构图

public interface Animal {

    public void show();
}

public class Cattle implements Animal {

    public static Cattle instance = null;

    public static Cattle getInstance() {
        if (instance == null) {
            instance = new Cattle();
        }
        return instance;
    }

    private Cattle() {

    }

    @Override
    public void show() {
        System.out.println("这是新产出的🐮");
    }
}

public class Horse implements Animal {
    public static Horse instance = null;

    private Horse() {

    }

    public static Horse getInstance() {
        if (instance == null) {
            instance = new Horse();
        }
        return instance;
    }

    @Override
    public void show() {
        System.out.println("这是生产出的🐴");
    }
}

public interface AnimalFarm {

    public Animal produceAnimal();
}

public class CattleFarm implements AnimalFarm {

    @Override
    public Animal produceAnimal() {
        return Cattle.getInstance();
    }
}

public class HorseFarm implements AnimalFarm {

    @Override
    public Animal produceAnimal() {
        return Horse.getInstance();
    }
}

public class FactoryTest {
    public static void main(String[] args) {
        HorseFarm horseFarm = new HorseFarm();
        horseFarm.produceAnimal().show();

        CattleFarm cattleFarm = new CattleFarm();
        cattleFarm.produceAnimal().show();
    }
}

抽象工厂模式

定义

是一种为访问类提供一个创建一组相关或相互依赖对象的接口,且访问类无须指定所要产品的具体类就能得到同族的不同等级的产品的模式结构。

抽象工厂模式是工厂方法模式的升级版本,工厂方法模式只生产一个等级的产品,而抽象工厂模式可生产多个等级的产品。

电器工厂的产品等级与产品族

模式结构

  1. 抽象工厂(Abstract Factory):提供了创建产品的接口,它包含多个创建产品的方法 newProduct(),可以创建多个不同等级的产品。
  2. 具体工厂(Concrete Factory):主要是实现抽象工厂中的多个抽象方法,完成具体产品的创建。
  3. 抽象产品(Product):定义了产品的规范,描述了产品的主要特性和功能,抽象工厂模式有多个抽象产品。
  4. 具体产品(ConcreteProduct):实现了抽象产品角色所定义的接口,由具体工厂来创建,它 同具体工厂之间是多对一的关系。

抽象工厂模式的结构图

应用场景

  1. 当需要创建的对象是一系列相互关联或相互依赖的产品族时,如电器工厂中的电视机、洗衣机、空调等。
  2. 系统中有多个产品族,但每次只使用其中的某一族产品。如有人只喜欢穿某一个品牌的衣服和鞋。
  3. 系统中提供了产品的类库,且所有产品的接口相同,客户端不依赖产品实例的创建细节和内部结构。

应用实例

农场中除了像畜牧场一样可以养动物,还可以培养植物,如养马、养牛、种菜、种水果等。

本例用抽象工厂模式来设计两个农场,一个是韶关农场用于养牛和种菜,一个是上饶农场用于养马和种水果,可以在以上两个农场中定义一个生成动物的方法 newAnimal() 和一个培养植物的方法 newPlant()。

对马类、牛类、蔬菜类和水果类等具体产品类,由于要显示它们的图像,所以定义一个 show() 方法。

农场类的结构图

建造者模式

定义

指将一个复杂对象的构造与它的表示分离,使同样的构建过程可以创建不同的表示。即产品的组成部分是不变的,但每一部分是可以灵活选择的。

应用场景

  • 创建的对象较复杂,由多个部件构成,各部件面临着复杂的变化,但构件间的建造顺序是稳定的。
  • 创建复杂对象的算法独立于该对象的组成部分以及它们的装配方式,即产品的构建过程和最终的表示是独立的。

模式结构

  1. 产品角色(Product):它是包含多个组成部件的复杂对象,由具体建造者来创建其各个滅部件。
  2. 抽象建造者(Builder):它是一个包含创建产品各个子部件的抽象方法的接口,通常还包含一个返回复杂产品的方法 getResult()。
  3. 具体建造者(Concrete Builder):实现 Builder 接口,完成复杂产品的各个部件的具体创建方法。
  4. 指挥者(Director):它调用建造者对象中的部件构造与装配方法完成复杂对象的创建,在指挥者中不涉及具体产品的信息。

建造者模式的结构图

应用实例

客厅装修是一个复杂的过程,它包含墙体的装修、电视机的选择、沙发的购买与布局等。客户把装修要求告诉项目经理,项目经理指挥装修工人一步步装修,最后完成整个客厅的装修与布局,所以本实例用建造者模式实现比较适合。

客厅装修的结构图

public class Parlour {

    public static Parlour instance;

    private Parlour() {

    }

    protected static Parlour getInstance() {
        if (instance == null) {
            instance = new Parlour();
        }
        return instance;
    }

    private String TV;

    private String wall;

    private String sofa;

    public void setTV(String TV) {
        this.TV = TV;
    }

    public void setWall(String wall) {
        this.wall = wall;
    }

    public void setSofa(String sofa) {
        this.sofa = sofa;
    }

    public void showParlour() {
        String parlour = "in this parlour, sofa is " + sofa
                + ", TV is " + TV + ", wall is " + wall;
        System.out.println(parlour);
    }
}

abstract class Decorator {

    protected Parlour parlour = null;

    public abstract void buildWall();

    public abstract void buildTV();

    public abstract void buildSofa();

    public Decorator() {
        getParlourDecoration();
    }

    public Parlour getParlourDecoration() {
        if (parlour == null) {
            parlour = Parlour.getInstance();
        }
        return parlour;
    }
}

public class ConcreteDecorator1 extends Decorator {

    @Override
    public void buildWall() {
        super.parlour.setWall("墙壁1-中国风墙壁");
    }

    @Override
    public void buildTV() {
        super.parlour.setTV("电视1-三星UHD电视");
    }

    @Override
    public void buildSofa() {
        super.parlour.setSofa("沙发-中国风实木沙发");
    }
}

public class ConcreteDecorate2 extends Decorator {

    @Override
    public void buildWall() {
        super.parlour.setWall("墙壁2-ins风格的墙壁");
    }

    @Override
    public void buildTV() {
        super.parlour.setTV("电视2-康佳液晶电视");
    }

    @Override
    public void buildSofa() {
        super.parlour.setSofa("沙发2-ins风格沙发");
    }
}

public class ProjectManager {

    private Decorator mDecorator;

    public ProjectManager(Decorator decorator) {
        mDecorator = decorator;
    }

    public void decorate() {
        mDecorator.buildSofa();
        mDecorator.buildWall();
        mDecorator.buildTV();
        mDecorator.parlour.showParlour();
    }
}

public class ProjectManagerTest {

    public static void main(String[] args) {
        ProjectManager pm = new ProjectManager(new ConcreteDecorate2());
        pm.decorate();
        pm = new ProjectManager(new ConcreteDecorator1());
        pm.decorate();
    }
}

结构型模式

结构型模式描述如何将类或对象按某种布局组成更大的结构。它分为类结构型模式和对象结构型模式,前者采用继承机制来组织接口和类,后者釆用组合或聚合来组合对象。

结构型模式分为以下 7 种:

  1. 代理(Proxy)模式:为某对象提供一种代理以控制对该对象的访问。即客户端通过代理间接地访问该对象,从而限制、增强或修改该对象的一些特性。
  2. 适配器(Adapter)模式:将一个类的接口转换成客户希望的另外一个接口,使得原本由于接口不兼容而不能一起工作的那些类能一起工作。
  3. 桥接(Bridge)模式:将抽象与实现分离,使它们可以独立变化。它是用组合关系代替继承关系来实现的,从而降低了抽象和实现这两个可变维度的耦合度。
  4. 装饰(Decorator)模式:动态地给对象增加一些职责,即增加其额外的功能。
  5. 外观(Facade)模式:为多个复杂的子系统提供一个一致的接口,使这些子系统更加容易被访问。
  6. 享元(Flyweight)模式:运用共享技术来有效地支持大量细粒度对象的复用。
  7. 组合(Composite)模式:将对象组合成树状层次结构,使用户对单个对象和组合对象具有一致的访问性。

代理模式

定义

由于某些原因需要给某对象提供一个代理以控制对该对象的访问。这时,访问对象不适合或者不能直接引用目标对象,代理对象作为访问对象和目标对象之间的中介。

应用场景

  • 远程代理,这种方式通常是为了隐藏目标对象存在于不同地址空间的事实,方便客户端访问。例如,用户申请某些网盘空间时,会在用户的文件系统中建立一个虚拟的硬盘,用户访问虚拟硬盘时实际访问的是网盘空间。
  • 虚拟代理,这种方式通常用于要创建的目标对象开销很大时。例如,下载一幅很大的图像需要很长时间,因某种计算比较复杂而短时间无法完成,这时可以先用小比例的虚拟代理替换真实的对象,消除用户对服务器慢的感觉。
  • 安全代理,这种方式通常用于控制不同种类客户对真实对象的访问权限。
  • 智能指引,主要用于调用目标对象时,代理附加一些额外的处理功能。例如,增加计算真实对象的引用次数的功能,这样当该对象没有被引用时,就可以自动释放它。
  • 延迟加载,指为了提高系统的性能,延迟对目标的加载。例如,Hibernate 中就存在属性的延迟加载和关联表的延时加载。

模式结构

  1. 抽象主题(Subject)类:通过接口或抽象类声明真实主题和代理对象实现的业务方法。

  2. 真实主题(Real Subject)类:实现了抽象主题中的具体业务,是代理对象所代表的真实对象,是最终要引用的对象。

  3. 代理(Proxy)类:提供了与真实主题相同的接口,其内部含有对真实主题的引用,它可以访问、控制或扩展真实主题的功能。

代理模式的结构图

应用实例

韶关“天街e角”公司是一家婺源特产公司的代理公司,用代理模式实现。

婺源特产公司”经营许多婺源特产,它是真实主题,提供了显示特产的 display() 方法,而韶关“天街e角”公司是婺源特产公司特产的代理,通过调用婺源特产公司的 display() 方法显示代理产品,当然它可以增加一些额外的处理,如包裝或加价等。客户可通过“天街e角”代理公司间接访问“婺源特产公司”的产品

韶关“天街e角”公园的结构图

interface Specialty {

    public void display();
}

public class SGProxy implements Specialty {

    private WYSpecialty mWYSpecialty;

    public SGProxy() {
        if (mWYSpecialty == null) {
            mWYSpecialty = WYSpecialty.getInstance();
        }
    }

    public void preRequest() {
        System.out.println("韶关代理请求婺源特产信息之前,开展的准备工作中ing....");
    }

    @Override
    public void display() {
        preRequest();
        mWYSpecialty.display();
        postRequest();
    }

    public void postRequest() {
        System.out.println("韶关代理请求婺源特产信息结束");
    }

}

public class WYSpecialty implements Specialty {

    public static WYSpecialty instance;

    private WYSpecialty() {

    }

    public static WYSpecialty getInstance() {
        if (instance == null) {
            instance = new WYSpecialty();
        }
        return instance;
    }

    @Override
    public void display() {
        System.out.println("婺源特产主要有:" + "\n" +
                "1. 荷包红鲤鱼产于婺源民间,色泽金红,头小尾短,背高体宽,腹厚肥大,状似荷包,故称荷包红鲤鱼。" + "\n"
                + "2. 婺源绿茶历史悠久,唐代著名茶叶专家陆羽在《茶经》中就有“歙州茶生于婺源山 谷”的记载。" + "\n"
                + "3. 婺源制墨,最早始于南唐,因婺源旧属安徽新安郡、徽州,故婺源墨又称“新安墨”、“徽墨”。" + "\n"
                + "4. 婺源汽糕是江西省上饶市婺源县的特产。婺源县汽糕表面鲜香油亮,光亮透亮,中间布满了蜂窝状的气孔。" );
    }
}

public class ProxyTest {

    public static void main(String[] args) {
        SGProxy sgProxy = new SGProxy();
        sgProxy.display();
    }
}

动态代理模式

动态代理模式的结构图

public interface AbstractSubject {

    public void display();

    public int compute(int x, int y);
}

public class RealSpecialty implements AbstractSubject {

    @Override
    public void display() {
        System.out.println("这是使用Spring AOP动态代理模式访问婺源特产对象");
        System.out.println("婺源特产主要有:" + "\n" +
                "1. 荷包红鲤鱼产于婺源民间,色泽金红,头小尾短,背高体宽,腹厚肥大,状似荷包,故称荷包红鲤鱼。" + "\n"
                + "2. 婺源绿茶历史悠久,唐代著名茶叶专家陆羽在《茶经》中就有“歙州茶生于婺源山 谷”的记载。" + "\n"
                + "3. 婺源制墨,最早始于南唐,因婺源旧属安徽新安郡、徽州,故婺源墨又称“新安墨”、“徽墨”。" + "\n"
                + "4. 婺源汽糕是江西省上饶市婺源县的特产。婺源县汽糕表面鲜香油亮,光亮透亮,中间布满了蜂窝状的气孔。");
    }

    @Override
    public int compute(int x, int y) {
        int rs = x + y;
        return rs;
    }
}

public class DynamicProxy implements InvocationHandler {

    private Object object;

    public DynamicProxy(Object object) {
        this.object = object;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        /*这里一定要将method调用invoke()方法的结果obj返回,不然在调用AbstractSubject的一些有返回值的方法时会报空指针异常*/
        return method.invoke(object, args);
    }
}


public class ProxyTest {

    public static void main(String[] args) {
        // Spring AOP动态代理
        AbstractSubject sub = null;
        InvocationHandler handler = null;
        handler = new DynamicProxy(new RealSpecialty());
        sub = (AbstractSubject) Proxy.newProxyInstance(
                AbstractSubject.class.getClassLoader(),
                new Class[]{AbstractSubject.class},
                handler);
        sub.display();
        System.out.println("计算结果:" + sub.compute(23,45));
    }
}

适配器模式

定义

将一个类的接口转换成客户希望的另外一个接口,使得原本由于接口不兼容而不能一起工作的那些类能一起工作。

模式结构

  1. 目标(Target)接口:当前系统业务所期待的接口,它可以是抽象类或接口。
  2. 适配者(Adaptee)类:它是被访问和适配的现存组件库中的组件接口。
  3. 适配器(Adapter)类:它是一个转换器,通过继承或引用适配者的对象,把适配者接口转换成目标接口,让客户按目标接口的格式访问适配者。

类适配器模式的结构图如图

类适配器模式的结构图

对象适配器模式的结构图如图

对象适配器模式的结构图

应用实例

新能源汽车的发动机有电能发动机(Electric Motor)和光能发动机(Optical Motor)等,各种发动机的驱动方法不同,例如,电能发动机的驱动方法 electricDrive() 是用电能驱动,而光能发动机的驱动方法 opticalDrive() 是用光能驱动,它们是适配器模式中被访问的适配者。

客户端希望用统一的发动机驱动方法 drive() 访问这两种发动机,所以必须定义一个统一的目标接口 Motor,然后再定义电能适配器(Electric Adapter)和光能适配器(Optical Adapter)去适配这两种发动机。

发动机适配器的结构图

public interface Motor {

    public void drive();
}

public class ElectricMotor {

    public static ElectricMotor instance;

    private ElectricMotor() {

    }

    public static ElectricMotor getInstance() {
        if (instance == null) {
            instance = new ElectricMotor();
        }
        return instance;
    }

    public void electricDrive() {
        System.out.println("电力驱动的特斯拉汽车");
    }
}

public class ElectricMotorAdapter implements Motor {

    private ElectricMotor mMotor;

    public ElectricMotorAdapter() {
        if (mMotor == null) {
            mMotor = ElectricMotor.getInstance();
        }
    }

    @Override
    public void drive() {
        mMotor.electricDrive();
    }
}

public class OpticalMotor {

    public static OpticalMotor instance;

    private OpticalMotor() {

    }

    public static OpticalMotor getInstance() {
        if (instance == null) {
            instance = new OpticalMotor();
        }
        return instance;
    }

    public void opticalDrive() {
        System.out.println("光能驱动的丰田汽车");
    }
}

public class OpticalMotorAdapter implements Motor {

    private OpticalMotor mMotor;

    public OpticalMotorAdapter() {
        if (mMotor == null) {
            mMotor = OpticalMotor.getInstance();
        }
    }

    @Override
    public void drive() {
        mMotor.opticalDrive();
    }
}

public class AdapterTest {

    public static void main(String[] args) {
        Motor motor = new ElectricMotorAdapter();
        motor.drive();
        motor = new OpticalMotorAdapter();
        motor.drive();
    }
}

装饰器模式

装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构。这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装。

我们将创建一个 Shape 接口和实现了 Shape 接口的实体类。
然后我们创建一个实现了 Shape 接口的抽象装饰类 ShapeDecorator,并把 Shape 对象作为它的实例变量。
RedShapeDecorator 是实现了 ShapeDecorator 的实体类。
DecoratorPatternDemo 类使用 RedShapeDecorator 来装饰 Shape 对象。
在这里插入图片描述

策略模式

策略模式定义了一系列的算法,并封装起来,提供针对同一类型问题的多种处理方式。

创建一个定义活动的 Strategy 接口和实现了 Strategy 接口的实体策略类。Context 是一个使用了某种策略的类。
StrategyPatternDemo,我们的演示类使用 Context 和策略对象来演示 Context 在它所配置或使用的策略改变时的行为变化。
在这里插入图片描述

Android源码中的策略模式

想要控制动画的速度,让它加速或者减速运动,就可以通过插值器实现。对于插值器就是策略模式的典型应用。

调用Animation.getTransformation(currentTime, outTransformation) 来计算和应用动画效果。

public boolean getTransformation(long currentTime, Transformation outTransformation) {
...
    //计算时间流逝的百分比
    float normalizedTime;
    if (duration != 0) {
        normalizedTime = ((float) (currentTime - (mStartTime + startOffset))) /
                (float) duration;
    } else {
        // time is a step-change with a zero duration
        normalizedTime = currentTime < mStartTime ? 0.0f : 1.0f;
    }
    //动画是否已经完成
    final boolean expired = normalizedTime >= 1.0f || isCanceled();
    mMore = !expired;

...

    if ((normalizedTime >= 0.0f || mFillBefore) && (normalizedTime <= 1.0f || mFillAfter)) {
...
        //通过插值器来获取动画执行的百分比 看到策略模式的影子
        final float interpolatedTime = mInterpolator.getInterpolation(normalizedTime);  //1
        
        //根据获取的动画执行的百分比,来应用动画效果
        applyTransformation(interpolatedTime, outTransformation); //2
    }

    return mMore;
}

Android系统默认给我们提供了几种插值器

// 线性插值器
public class LinearInterpolator implements Interpolator{
    public float getInterpolation(float input) {
        return input;
    }
}

// 加速插值器
public class AccelerateInterpolator extends BaseInterpolator implements NativeInterpolatorFactory {
    private final float mFactor;
    private final double mDoubleFactor;

    public AccelerateInterpolator() {
        mFactor = 1.0f;
        mDoubleFactor = 2.0;
    }

    public float getInterpolation(float input) {
        //mFactor默认是1.0f。
        if (mFactor == 1.0f) {
            return input * input;
        } else {
            return (float)Math.pow(input, mDoubleFactor);
        }
    }
}

调用动画类的applyTransformation 来将属性值用到View上。

@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
    float sx = 1.0f;
    float sy = 1.0f;
    float scale = getScaleFactor();
    //通过动画百分比来计算当时目标值
    if (mFromX != 1.0f || mToX != 1.0f) {
        sx = mFromX + ((mToX - mFromX) * interpolatedTime);
    }
    if (mFromY != 1.0f || mToY != 1.0f) {
        sy = mFromY + ((mToY - mFromY) * interpolatedTime);
    }
    //通过Matrix实现View的缩放
    if (mPivotX == 0 && mPivotY == 0) {
        t.getMatrix().setScale(sx, sy);
    } else {
        t.getMatrix().setScale(sx, sy, scale * mPivotX, scale * mPivotY);
    }
}

观察者模式

rxjava,eventbus都使用这个模式
当对象间存在一对多关系时,则使用观察者模式(Observer Pattern)。比如,当一个对象被修改时,则会自动通知依赖它的对象。

优点:观察者和被观察者之间松耦合;有一套自己的触发机制
缺点:被观察者有很多个观察者,则观察者收到消息会很慢;被观察者和观察者如果存在循环调用,则可能崩溃
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值