• 常用的设计模式


    以下内容走其他网站找来,需要完整版私信我

    1.为什么需要学习设计模式

    设计模式(Design pattern)代表了最佳的实践,是很多优秀的软件开发人员的经验总结,是解决特定问题的解决方案。它并不是语法规定,也不拘泥于特定语言。 恰当的使用设计模式可以代码的可复用性,可维护性,可扩展性,健壮性及安全性,这些都是系统非常重要的非功能性需求。
    设计模式的广泛使用起始于1995年,GOF(四人帮)出版的《设计模式:可复用面向对象软件基础》。
    本部分相关的示例代码:

    https://gitee.com/lisenaq/patterndemo.git
    
    • 1

    2.什么是设计模式

    设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。 毫无疑问,设计模式于己于他人于系统都是多赢的,设计模式使代码编制真正工程化,设计模式是软件工程的基石,如同大厦的一块块砖石一样。项目中合理的运用设计模式可以完美的解决很多问题,每种模式在现在中都有相应的原理来与之对应,每一个模式描述了一个在我们周围不断重复发生的问题,以及该问题的核心解决方案,这也是它能被广泛应用的原因

    3.设计模式的六大原则

    在上一章我简单提到过六大原则
    一、开闭原则(Open Close Principle)
    二、里氏代换原则(Liskov Substitution Principle)
    三、依赖倒转原则(Dependence Inversion Principle)
    四、接口隔离原则(Interface Segregation Principle)
    五、迪米特法则(最少知道原则)(Demeter Principle)
    六、合成复用原则(Composite Reuse Principle)
    六大原则详细讲解点击详细链接

    4.设计模式的三大类

    总体来说设计模式分为三大类:
    创建型模式(5种):工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。
    结构型模式(7种):适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。
    行为型模式(11种):策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。
    在这里插入图片描述

    5.创建型模式

    5.1创建模式的特点

    它的主要特点是“将对象的创建与使用分离”。这样可以降低系统的耦合度,使用者不需要关注对象的创建细节,对象的创建由相关的工厂来完成。就像我们去商场购买商品时,不需要知道商品是怎么生产出来一样,因为它们由专门的厂商生产。

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

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

    5.3单例模式

    5.3.1单例模式的定义与特点

    单例(Singleton)模式的定义:指一个类只有一个实例,且该类能自行创建这个实例的一种模式。例如,Windows 中只能打开一个任务管理器,这样可以避免因打开多个任务管理器窗口而造成内存资源的浪费,或出现各个窗口显示内容的不一致等错误。

    5.3.2单例模式的优点和缺点

    单例模式的优点:
    • 单例模式可以保证内存里只有一个实例,减少了内存的开销。
    • 可以避免对资源的多重占用。
    • 单例模式设置全局访问点,可以优化和共享资源的访问。

    单例模式的缺点:
    • 单例模式一般没有接口,扩展困难。如果要扩展,则除了修改原来的代码,没有第二种途径,违背开闭原则。
    • 在并发测试中,单例模式不利于代码调试。在调试过程中,如果单例中的代码没有执行完,也不能模拟生成一个新的对象。
    • 单例模式的功能代码通常写在一个类中,如果功能设计不合理,则很容易违背单一职责原则。

    5.3.3单例模式的结构与实现

    单例模式是设计模式中最简单的模式之一。通常,普通类的构造函数是公有的,外部类可以通过“new 构造函数()”来生成多个实例。但是,如果将类的构造函数设为私有的,外部类就无法调用该构造函数,也就无法生成多个实例。这时该类自身必须定义一个静态私有实例,并向外提供一个静态的公有函数用于创建或获取该静态私有实例。

    5.3.3.1单例模式的结构

    单例模式的主要角色如下。
    • 单例类:包含一个实例且能自行创建这个实例的类。
    • 访问类:使用单例的类。
    在这里插入图片描述

    5.3.3.2单例模式的实现

    5.3.3.2.1饿汉模式

    这种直线方式简单,且是线程安全的。

    /**
     * 单例模式,饥饿加载
     */
    public class SingletonDemo {
    
        //1. 需要有一个私有的构造函数,防止该类通过new的方式创建实例
        private SingletonDemo(){}
    
        //2. 饥饿模式,首先生成一个实例
        private static final SingletonDemo instance = new SingletonDemo();
    
        //3. 静态方法,用于获取已经生成的实例
        public static SingletonDemo getInstance() {
            return instance;
        }
    
        public String hello(String name) {
            return "hello " + name;
        }
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    5.3.3.2.1懒汉模式

    第一种写法:
    注意: 这种方式在多线程访问时会有问题。

    /**
     * 单例模式: 懒汉式
     */
    public class SingletonDemo02 {
    
        private SingletonDemo02(){}
    
        private static SingletonDemo02 singletonDemo02 = null;
    
        public static SingletonDemo02 getInstance() {
    
            if (singletonDemo02 == null) {
                singletonDemo02 = new SingletonDemo02();
            }
    
            return singletonDemo02;
        }
    
        public String hello(String name) {
            return "hello " + name;
        }
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    第二种写法:

    /**
     * 单例模式: 懒汉式,线程安全,但性能较低
     */
    public class SingletonDemo03 {
    
        private SingletonDemo03() {
        }
    
        private static SingletonDemo03 singletonDemo03 = null;
    
        public static synchronized SingletonDemo03 getInstance(){
            if(singletonDemo03 == null) {
                singletonDemo03 = new SingletonDemo03();
            }
            return singletonDemo03;
        }
    
        public String hello(String name) {
            return "hello " + name;
        }
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    第三种写法
    该方式依然会有线程安全问题

    public class SingletonDemo03 {
    
        private SingletonDemo03() {
        }
    
        private static SingletonDemo03 singletonDemo03 = null;
    
        public static  SingletonDemo03 getInstance(){
            //系统减小同步块来提升性能,可以吗?
            if(singletonDemo03 == null) {
                synchronized (SingletonDemo03.class) {
                    singletonDemo03 = new SingletonDemo03();
                }
            }
            return singletonDemo03;
        }
    
        public String hello(String name) {
            return "hello " + name;
        }
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    第四种写法

    /**
     * 单例模式: 懒汉式,双重检查单例
     */
    public class SingletonDemo03 {
    
        private SingletonDemo03(){
        }
    
        private static SingletonDemo03 singletonDemo03 = null;
    
        public static  SingletonDemo03 getInstance(){
            //减小同步块,并使用双重检查来保证线程安装,可以吗?
            if(singletonDemo03 == null) {
                synchronized (SingletonDemo03.class) {
                    if(singletonDemo03 == null) {
                        singletonDemo03 = new SingletonDemo03();
                    }
                }
            }
            return singletonDemo03;
        }
    
        public String hello(String name) {
            return "hello " + name;
        }
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    第五种写法

    /**
     * 单例模式: 懒加载, 线程安全
     */
    public class SingletonDemo04 {
    
        //阻止外部实例化
        private SingletonDemo04(){
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    
        //使用静态内部类来使用一个SingletonDemo04对象
        private static class SingletonDemoHolder {
            private final static SingletonDemo04 instance = new SingletonDemo04();
        }
    
        public static SingletonDemo04 getInstance() {
            return SingletonDemoHolder.instance;
        }
    
        public String hello(String name) {
            return "hello " + name;
        }
    
    }
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    第六种写法
    可以保证单例,且线程安全

    public enum  SingletonDemo05 {
    
        INSTANCE;
    
        public String hello(String name) {
            return "hello " + name;
        }
    	
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    5.3工厂模式

    简介:
    现实生活中,原始社会自给自足(没有工厂),农耕社会小作坊(简单工厂,民间酒坊),工业革命流水线(工厂方法,自产自销),现代产业链代工厂(抽象工厂,富士康)。我们的项目代码同样是由简到繁一步一步迭代而来的,但对于调用者来说,却越来越简单。

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

    工厂模式不同的实现方式:
    简单工厂模式、工厂方法模式、抽象工厂模式

    简单工厂模式
    我们把被创建的对象称为“产品”,把创建产品的对象称为“工厂”。如果要创建的产品不多,只要一个工厂类就可以完成。在简单工厂模式中创建实例的方法通常为静态(static)方法,因此简单工厂模式(Simple Factory Pattern)又叫作静态工厂方法模式(Static Factory Method Pattern)。简单来说,简单工厂模式有一个具体的工厂类,可以生成多个不同的产品,属于创建型设计模式。简单工厂模式不在 GoF 23 种设计模式之列。简单工厂模式每增加一个产品就要增加一个具体产品类和一个对应的具体工厂类,这增加了系统的复杂度,违背了“开闭原则”。

    工厂方法模式
    “工厂方法模式”是对简单工厂模式的进一步抽象化,其好处是可以使系统在不修改原来代码的情况下引进新的产品,即满足开闭原则。

    5.3.1简单工厂模式

    简单工厂模式的优点和缺点
    优点:

    1. 工厂类包含必要的逻辑判断,可以决定在什么时候创建哪一个产品的实例。客户端可以免除直接创建产品对象的职责,很方便的创建出相应的产品。工厂和产品的职责区分明确。
    2. 客户端无需知道所创建具体产品的类名,只需知道参数即可。
    3. 也可以引入配置文件,在不修改客户端代码的情况下更换和添加新的具体产品类。
      缺点:
    4. 简单工厂模式的工厂类单一,负责所有产品的创建,职责过重,一旦异常,整个系统将受影响。且工厂类代码会非常臃肿,违背高聚合原则。
    5. 使用简单工厂模式会增加系统中类的个数(引入新的工厂类),增加系统的复杂度和理解难度
    6. 系统扩展困难,一旦增加新产品不得不修改工厂逻辑,在产品类型较多时,可能造成逻辑过于复杂
    7. 简单工厂模式使用了 static 工厂方法,造成工厂角色无法形成基于继承的等级结构。

    简单工厂模式应用场景
    对于产品种类相对较少的情况,考虑使用简单工厂模式。使用简单工厂模式的客户端只需要传入工厂类的参数,不需要关心如何创建对象的逻辑,可以很方便地创建所需产品。

    简单工厂模式的结构与实现
    简单工厂模式的主要角色如下:
    • 简单工厂(SimpleFactory):是简单工厂模式的核心,负责实现创建所有实例的内部逻辑。工厂类的创建产品类的方法可以被外界直接调用,创建所需的产品对象。
    • 抽象产品(Product):是简单工厂创建的所有对象的父类,负责描述所有实例共有的公共接口。
    • 具体产品(ConcreteProduct):是简单工厂模式的创建目标。
    在这里插入图片描述

    public class Factory01 {
        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;
            }
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    5.3.2工厂方法模式

    介绍:
    在《简单工厂模式》一节我们介绍了简单工厂模式,提到了简单工厂模式违背了开闭原则,而“工厂方法模式”是对简单工厂模式的进一步抽象化,其好处是可以使系统在不修改原来代码的情况下引进新的产品,即满足开闭原则。

    工厂方法模式的优缺点:
    优点:
    用户只需要知道具体工厂的名称就可得到所要的产品,无须知道产品的具体创建过程。
    灵活性增强,对于新产品的创建,只需多写一个相应的工厂类。
    典型的解耦框架。高层模块只需要知道产品的抽象类,无须关心其他实现类,满足迪米特法则、依赖倒置原则和里氏替换原则。
    缺点:
    类的个数容易过多,增加复杂度
    增加了系统的抽象性和理解难度
    抽象产品只能生产一种产品,此弊端可使用抽象工厂模式解决。

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

    模式的结构与实现
    工厂方法模式由抽象工厂、具体工厂、抽象产品和具体产品等4个要素构成。本节来分析其基本结构和实现方法。

    1. 模式的结构
      工厂方法模式的主要角色如下。
      抽象工厂(Abstract Factory):提供了创建产品的接口,调用者通过它访问具体工厂的工厂方法 newProduct() 来创建产品。
      具体工厂(ConcreteFactory):主要是实现抽象工厂中的抽象方法,完成具体产品的创建。
      抽象产品(Product):定义了产品的规范,描述了产品的主要特性和功能。
      具体产品(ConcreteProduct):实现了抽象产品角色所定义的接口,由具体工厂来创建,它同具体工厂之间一一对应。
      在这里插入图片描述
      代码:
    package factory;
    
    public class Factory02 {
        public static void main(String[] args) {
            try {
                Product a;
                AbstractFactory af;
                af = (AbstractFactory) ReadXML1.getObject();
                a = af.newProduct();
                a.show();
            } catch (Exception e) {
                System.out.println(e.getMessage());
            }
        }
    }
    //抽象产品:提供了产品的接口
    interface Product {
        public void show();
    }
    //具体产品1:实现抽象产品中的抽象方法
    class ConcreteProduct1 implements Product {
        public void show() {
            System.out.println("具体产品1显示...");
        }
    }
    //具体产品2:实现抽象产品中的抽象方法
    class ConcreteProduct2 implements Product {
        public void show() {
            System.out.println("具体产品2显示...");
        }
    }
    //抽象工厂:提供了厂品的生成方法
    interface AbstractFactory {
        public Product newProduct();
    }
    //具体工厂1:实现了厂品的生成方法
    class ConcreteFactory1 implements AbstractFactory {
        public Product newProduct() {
            System.out.println("具体工厂1生成-->具体产品1...");
            return new ConcreteProduct1();
        }
    }
    //具体工厂2:实现了厂品的生成方法
    class ConcreteFactory2 implements AbstractFactory {
        public Product newProduct() {
            System.out.println("具体工厂2生成-->具体产品2...");
            return new ConcreteProduct2();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    package factory;
    
    import javax.xml.parsers.*;
    import org.w3c.dom.*;
    import java.io.*;
    class ReadXML1 {
        //该方法用于从XML配置文件中提取具体类类名,并返回一个实例对象
        public static Object getObject() {
            try {
                //创建文档对象
                DocumentBuilderFactory dFactory = DocumentBuilderFactory.newInstance();
                DocumentBuilder builder = dFactory.newDocumentBuilder();
                Document doc;
                doc = builder.parse(new File("src/FactoryMethod/config1.xml"));
                //获取包含类名的文本节点
                NodeList nl = doc.getElementsByTagName("className");
                Node classNode = nl.item(0).getFirstChild();
                String cName = "FactoryMethod." + classNode.getNodeValue();
                //System.out.println("新类名:"+cName);
                //通过类名生成实例对象并将其返回
                Class<?> c = Class.forName(cName);
                Object obj = c.newInstance();
                return obj;
            } catch (Exception e) {
                e.printStackTrace();
                return null;
            }
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    程序运行结果如下:

    具体工厂1生成–>具体产品1…

    具体产品1显示…

    如果将 XML 配置文件中的 ConcreteFactory1 改为 ConcreteFactory2,则程序运行结果如下:

    具体工厂2生成–>具体产品2…

    具体产品2显示…

    5.3.1抽象工厂模式

    同种类称为同等级,也就是说:工厂方法模式只考虑生产同等级的产品,但是在现实生活中许多工厂是综合型的工厂,能生产多等级(种类) 的产品,如农场里既养动物又种植物,电器厂既生产电视机又生产洗衣机或空调,大学既有软件专业又有生物专业等。

    抽象工厂模式将考虑多等级产品的生产,将同一个具体工厂所生产的位于不同等级的一组产品称为一个产品族,图 1 所示的是海尔工厂和 TCL 工厂所生产的电视机与空调对应的关系图。
    在这里插入图片描述模式的定义与特点
    抽象工厂(AbstractFactory)模式的定义:是一种为访问类提供一个创建一组相关或相互依赖对象的接口,且访问类无须指定所要产品的具体类就能得到同族的不同等级的产品的模式结构。

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

    使用抽象工厂模式一般要满足以下条件。
    系统中有多个产品族,每个具体工厂创建同一族但属于不同等级结构的产品。
    系统一次只可能消费其中某一族产品,即同族的产品一起使用。

    抽象工厂模式除了具有工厂方法模式的优点外,其他主要优点如下。
    可以在类的内部对产品族中相关联的多等级产品共同管理,而不必专门引入多个新的类来进行管理。
    当需要产品族时,抽象工厂可以保证客户端始终只使用同一个产品的产品组。
    抽象工厂增强了程序的可扩展性,当增加一个新的产品族时,不需要修改原代码,满足开闭原则。

    其缺点是:当产品族中需要增加一个新的产品时,所有的工厂类都需要进行修改。增加了系统的抽象性和理解难度。

    模式的结构与实现
    抽象工厂模式同工厂方法模式一样,也是由抽象工厂、具体工厂、抽象产品和具体产品等 4 个要素构成,但抽象工厂中方法个数不同,抽象产品的个数也不同。现在我们来分析其基本结构和实现方法。

    1. 模式的结构
      抽象工厂模式的主要角色如下。
      抽象工厂(Abstract Factory):提供了创建产品的接口,它包含多个创建产品的方法 newProduct(),可以创建多个不同等级的产品。
      具体工厂(Concrete Factory):主要是实现抽象工厂中的多个抽象方法,完成具体产品的创建。
      抽象产品(Product):定义了产品的规范,描述了产品的主要特性和功能,抽象工厂模式有多个抽象产品。
      具体产品(ConcreteProduct):实现了抽象产品角色所定义的接口,由具体工厂来创建,它同具体工厂之间是多对一的关系。
      在这里插入图片描述
      2. 模式的实现
      从图 2 可以看出抽象工厂模式的结构同工厂方法模式的结构相似,不同的是其产品的种类不止一个,所以创建产品的方法也不止一个。下面给出抽象工厂和具体工厂的代码。

    (1) 抽象工厂:提供了产品的生成方法。

    interface AbstractFactory {
        public Product1 newProduct1();
        public Product2 newProduct2();
    }
    
    • 1
    • 2
    • 3
    • 4

    (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();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    6.行文型模式

    行为型模式用于描述程序在运行时复杂的流程控制,即描述多个类或对象之间怎样相互协作共同完成单个对象都无法单独完成的任务,它涉及算法与对象间职责的分配。

    行为型模式分为类行为模式和对象行为模式,前者采用继承机制来在类间分派行为,后者采用组合或聚合在对象间分配行为。由于组合关系或聚合关系比继承关系耦合度低,满足“合成复用原则”,所以对象行为模式比类行为模式具有更大的灵活性。

    行为型模式是 GoF 设计模式中最为庞大的一类,它包含以下 11 种模式。
    模板方法(Template Method)模式:定义一个操作中的算法骨架,将算法的一些步骤延迟到子类中,使得子类在可以不改变该算法结构的情况下重定义该算法的某些特定步骤。
    策略(Strategy)模式:定义了一系列算法,并将每个算法封装起来,使它们可以相互替换,且算法的改变不会影响使用算法的客户。
    命令(Command)模式:将一个请求封装为一个对象,使发出请求的责任和执行请求的责任分割开。
    职责链(Chain of Responsibility)模式:把请求从链中的一个对象传到下一个对象,直到请求被响应为止。通过这种方式去除对象之间的耦合。
    状态(State)模式:允许一个对象在其内部状态发生改变时改变其行为能力。
    观察者(Observer)模式:多个对象间存在一对多关系,当一个对象发生改变时,把这种改变通知给其他多个对象,从而影响其他对象的行为。
    中介者(Mediator)模式:定义一个中介对象来简化原有对象之间的交互关系,降低系统中对象间的耦合度,使原有对象之间不必相互了解。
    迭代器(Iterator)模式:提供一种方法来顺序访问聚合对象中的一系列数据,而不暴露聚合对象的内部表示。
    访问者(Visitor)模式:在不改变集合元素的前提下,为一个集合中的每个元素提供多种访问方式,即每个元素有多个访问者对象访问。
    备忘录(Memento)模式:在不破坏封装性的前提下,获取并保存一个对象的内部状态,以便以后恢复它。
    解释器(Interpreter)模式:提供如何定义语言的文法,以及对语言句子的解释方法,即解释器。

    以上 11 种行为型模式,除了模板方法模式和解释器模式是类行为型模式,其他的全部属于对象行为型模式,下面我们将详细介绍它们的特点、结构与应用。

    6.1责任链模式

    在现实生活中,一个事件需要经过多个对象处理是很常见的场景。例如,采购审批流程、请假流程等。公司员工请假,可批假的领导有部门负责人、副总经理、总经理等,但每个领导能批准的天数不同,员工必须根据需要请假的天数去找不同的领导签名,也就是说员工必须记住每个领导的姓名、电话和地址等信息,这无疑增加了难度。

    在计算机软硬件中也有相关例子,如总线网中数据报传送,每台计算机根据目标地址是否同自己的地址相同来决定是否接收;还有异常处理中,处理程序根据异常的类型决定自己是否处理该异常;还有 Struts2 的拦截器、JSP 和 Servlet 的 Filter 等,所有这些,都可以考虑使用责任链模式来实现。

    模式的定义与特点
    责任链(Chain of Responsibility)模式的定义:为了避免请求发送者与多个请求处理者耦合在一起,于是将所有请求的处理者通过前一对象记住其下一个对象的引用而连成一条链;当有请求发生时,可将请求沿着这条链传递,直到有对象处理它为止。

    注意:责任链模式也叫职责链模式。

    在责任链模式中,客户只需要将请求发送到责任链上即可,无须关心请求的处理细节和请求的传递过程,请求会自动进行传递。所以责任链将请求的发送者和请求的处理者解耦了。

    责任链模式的优缺点:
    责任链模式是一种对象行为型模式,其主要优点如下。
    降低了对象之间的耦合度。该模式使得一个对象无须知道到底是哪一个对象处理其请求以及链的结构,发送者和接收者也无须拥有对方的明确信息。
    增强了系统的可扩展性。可以根据需要增加新的请求处理类,满足开闭原则。
    增强了给对象指派职责的灵活性。当工作流程发生变化,可以动态地改变链内的成员或者调动它们的次序,也可动态地新增或者删除责任。
    责任链简化了对象之间的连接。每个对象只需保持一个指向其后继者的引用,不需保持其他所有处理者的引用,这避免了使用众多的 if 或者 if···else 语句。
    责任分担。每个类只需要处理自己该处理的工作,不该处理的传递给下一个对象完成,明确各类的责任范围,符合类的单一职责原则。

    其主要缺点如下。
    不能保证每个请求一定被处理。由于一个请求没有明确的接收者,所以不能保证它一定会被处理,该请求可能一直传到链的末端都得不到处理。
    对比较长的职责链,请求的处理可能涉及多个处理对象,系统性能将受到一定影响。
    职责链建立的合理性要靠客户端来保证,增加了客户端的复杂性,可能会由于职责链的错误设置而导致系统出错,如可能会造成循环调用。

    模式的结构与实现

    1. 模式的结构
      职责链模式主要包含以下角色。
      抽象处理者(Handler)角色:定义一个处理请求的接口,包含抽象处理方法和一个后继连接。
      具体处理者(Concrete Handler)角色:实现抽象处理者的处理方法,判断能否处理本次请求,如果可以处理请求则处理,否则将该请求转给它的后继者。
      客户类(Client)角色:创建处理链,并向链头的具体处理者对象提交请求,它不关心处理细节和请求的传递过程。

    责任链模式的本质是解耦请求与处理,让请求在处理链中能进行传递与被处理;理解责任链模式应当理解其模式,而不是其具体实现。责任链模式的独到之处是将其节点处理者组合成了链式结构,并允许节点自身决定是否进行请求处理或转发,相当于让请求流动起来。

    在这里插入图片描述
    2. 模式的实现
    职责链模式的实现代码如下:

    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("没有人处理该请求!");
                }
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51

    程序运行结果如下:

    具体处理者2负责处理该请求!

    在上面代码中,我们把消息硬编码为 String 类型,而在真实业务中,消息是具备多样性的,可以是 int、String 或者自定义类型。因此,在上面代码的基础上,可以对消息类型进行抽象 Request,增强了消息的兼容性。

    6.2观察者模式(Obsever)

    在现实世界中,许多对象并不是独立存在的,其中一个对象的行为发生改变可能会导致一个或者多个其他对象的行为也发生改变。例如,某种商品的物价上涨时会导致部分商家高兴,而消费者伤心;还有,当我们开车到交叉路口时,遇到红灯会停,遇到绿灯会行。这样的例子还有很多,例如,股票价格与股民、微信公众号与微信用户、气象局的天气预报与听众、小偷与警察等。

    在软件世界也是这样,例如,Excel 中的数据与折线图、饼状图、柱状图之间的关系;MVC 模式中的模型与视图的关系;事件模型中的事件源与事件处理者。所有这些,如果用观察者模式来实现就非常方便。

    模式的定义与特点
    观察者(Observer)模式的定义:指多个对象间存在一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。这种模式有时又称作发布-订阅模式、模型-视图模式,它是对象行为型模式。

    观察者模式是一种对象行为型模式,其主要优点如下。
    降低了目标与观察者之间的耦合关系,两者之间是抽象耦合关系。符合依赖倒置原则。
    目标与观察者之间建立了一套触发机制。

    它的主要缺点如下。
    目标与观察者之间的依赖关系并没有完全解除,而且有可能出现循环引用。
    当观察者对象很多时,通知的发布会花费很多时间,影响程序的效率。

    模式的结构与实现
    实现观察者模式时要注意具体目标对象和具体观察者对象之间不能直接调用,否则将使两者之间紧密耦合起来,这违反了面向对象的设计原则。

    1. 模式的结构
      观察者模式的主要角色如下。
      抽象主题(Subject)角色:也叫抽象目标类,它提供了一个用于保存观察者对象的聚集类和增加、删除观察者对象的方法,以及通知所有观察者的抽象方法。
      具体主题(Concrete Subject)角色:也叫具体目标类,它实现抽象目标中的通知方法,当具体主题的内部状态发生改变时,通知所有注册过的观察者对象。
      抽象观察者(Observer)角色:它是一个抽象类或接口,它包含了一个更新自己的抽象方法,当接到具体主题的更改通知时被调用。
      具体观察者(Concrete Observer)角色:实现抽象观察者中定义的抽象方法,以便在得到目标的更改通知时更新自身的状态。
      在这里插入图片描述
    2. 模式的实现
      观察者模式的实现代码如下:
    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作出反应!");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51

    程序运行结果如下:

    具体目标发生改变…


    具体观察者1作出反应!
    具体观察者2作出反应!

  • 相关阅读:
    信息系统项目管理师核心考点(六十四)信息安全基础知识重要概念
    zynq pl访问ps ddr
    在使用了spring-cloud-starter-gateway后,为什么还会发生cors问题
    vue3 在js中使用scss变量
    中国电子学会2023年09月份青少年软件编程Python等级考试试卷五级真题(含答案)
    CFD网格质量评估标准
    35岁程序员被裁员,这半年他的故事
    设计模式之代理模式(十一)
    杜教筛练习题
    PostgreSQL、KingBase 数据库 ORDER BY LIMIT 查询缓慢案例
  • 原文地址:https://blog.csdn.net/Champion_me/article/details/125402395