• 设计模式学习记录


    设计模式

    UML图

    ------> 依赖

    ——>关联

    -------▲ 实现

    —–—▲ 继承

    🔺———> 聚合

    ▲———> 组合(关联性更强)

    一、策略模式(行为型)

    策略模式:是一种定义一系列算法的方法,从概念上来看,所有这些算法完成的都是相同的工作,只是实现不同。

    策略模式的Strategy类层次为Context定义了一系列的可供重用的算法和行为。在基本的策略模式中,选择所用具体实现的职责由客户端对象承担,并转给策略模式Context对象

    二、单一职责

    对于一个类来说,应该仅有一个引起它变化的原因。

    将一个应用的功能剥离开(例如:视图和业务逻辑),否则将这些职责耦合在一起,会导致这个应用很脆弱。耦合性高,代码复用性低。

    比如:俄罗斯方块游戏:可以分为界面窗体类+游戏逻辑类(旋转、移动、下落、堆积)

    三、开闭原则

    对于软件实体来说,应该可以对外扩展,但是不可修改。

    在我们最初编写代码时,假设变化不会发生。

    当变化发生时,我们就创建抽象(接口、抽象类)来隔离发生的同类变化。

    四、依赖倒转原则

    对于一个系统来说,每个部分的功能应该独立且单一,当出问题了只需要修改相应的部分即可(单一职责原则),并且应该尽量做到对外扩展开发,对外修改关闭。(开闭原则)。每个部分之间可以做到轻松的组合起来(依赖倒转原则,面向抽象编程(接口))

    在应用中不应该高层模块依赖底层模块,容易出现底层模块发生变化,高层也要变化。所以,无论是高层还是底层都应该依赖于接口,这样如果接口稳定,就不用担心其他模块受到影响。

    五、里氏替换原则

    一个软件实体如果使用的是一个父类的话,那么一定是用于它的子类,而且察觉不出来区别。

    任何父类出现的地方都可以用子类代替。

    由于子类型的可替换性,才使得使用夫类型的模块在无需修改的情况下可以扩展。

    六、装饰器模式(结构型)

    装饰器模式:动态的给一个对象添加一些额外的职责,就增加功能来说,装饰模式比生成子类更为灵活。在这里插入图片描述

    代码实现:

    Component:

    abstract class Component {
    	public abstract void Operation();
    }
    
    • 1
    • 2
    • 3

    ConcreteComponent(真实对象)

    class concreteComponent extends Component {
    	public override void Operation (){
    		Console.WriteLine("具体的对象操作");
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    Decorator(对原对象进行扩展)

    abstract class Decorator extends Component {
    	protected Compoment compoment;
        public void setCompoment(Compoment compoment) {
            this.compoment = compoment;
        }
    
        @Override
        public void Operation() {
            if(compoment != null) {
                compoment.Operation();
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    ConcreteDecoratorA(具体的装饰对象)

    public class ConcreteDecoratorA extends Decorator{
        private String strA = "A的功能扩展";
    
        @Override
        public void Operation() {
            compoment.Operation();
            strA = "A";
            System.out.println("装饰者A的装饰");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    ConcreteDecoratorB(具体的装饰对象)

    public class ConcreteDecoratorB extends Decorator{
        @Override
        public void Operation() {
    
            compoment.Operation();
            actionByB();
            System.out.println("装饰者B的装饰");
        }
        public void actionByB() {
            System.out.println("装饰者B特有的行为");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    客户端代码:

    public class Main {
        public static void main(String[] args) {
            Compoment p = new ConcreteComponent();       //创建一个具体对象
            ConcreteDecoratorA cona = new ConcreteDecoratorA();    //装饰者A
            cona.setCompoment(p);                        //设置装饰的对象
            ConcreteDecoratorB conb = new ConcreteDecoratorB();   //装饰者B
            conb.setCompoment(cona);                     //设置装饰的对象
            conb.Operation();                               
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    小节:

    装饰模式是为已有的功能动态地添加更多功能的一种方式,把每个要装饰的功能放在单独的类中,并让这个类包装它要装饰的对象。因此,客户代码就可以在运行时根据需要有选择地、按顺序的使用装饰功能包装对象。

    七、代理模式(结构型)

    为其他对象提供一种代理以控制对这个对象的访问。一般使用的场合:远程代理(反向代理)、虚拟代理(存放实例化需要很长时间的真实对象)、安全代理(用来控制真实对象访问时的权限)。

    在这里插入图片描述

    代码实现:

    公用接口

    abstract class Subject {
       public astract void Request();
    }
    
    • 1
    • 2
    • 3

    真实实体类:

    class RealSubject extends Subject {
       public override void Request() {
          Console.WriteLine("真实的请求");
       }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    代理类:

    class Proxy extends Subject {
       RealSubject realSubject;
       public override void Request() {
          if(realSubject == null) {
             realSubject = new RealSubject();
          }
          realSubject.Request();
       }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    客户端代码:

    public static void main (String[] args) {
       Proxy proxy = new Proxy();
       proxy.Request();
       Console.Read();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    装饰器模式和代理模式的区别:

    • 装饰器模式强调的时增强自身,在被装饰之后你能够在被增强的类上使用增强后的功能。而代理模式强调要让别人帮你去做一些本身于你业务没有太多的关系的职责(日志处理、设置缓存),将核心业务和边缘业务分离开,降低程序的耦合度。
    • 装饰模式是以对客户端透明的方式扩展对象的功能;代理模式则是给一个对象提供一个代理对象,由代理对象来控制对象对原有对象的使用。
    • 装饰器模式的功能由客户端根据需求进行选择,更灵活,而代理模式的功能已经写死了
    • 代理注重对原对象的控制(代理模式中目标对象的生命周期是代理对象控制的),不易于二次扩展。装饰器通过上层装饰,易于扩展。

    八、工厂方法模式(创建型)

    //1、简单工厂的结构、缺点

    如果我们要设计一个代码复用性高、扩展性高(开闭原则),并且基于依赖反转以及单一职责原则我们可以实现一个基类(运算抽象类),然后分别具体的加法类、减法类等重写运算类。最后实现一个简单工厂类,可以根据不同的规则创建不同的对象。

    但是,当我们要新增一个开根运算运算时,我们需要继承运算类,然后重写方法,最后修改工厂类逻辑,此处破坏了开闭原则,导致程序的耦合度过高。所以我们引入了工厂方法模式

    在这里插入图片描述

    ​ 简单工厂类结构图

    //2、简单工厂模式和策略模式的区别

    简单工厂模式和策略模式的区别:
    1. 用途不一样,简单工厂类是创建型模式,它的作用是创建对象。策略模式是行为型模式,作用是在许多行为中选择一种行为,关注的是行为的多样性。
    2. 解决的问题不同。简单工厂模式是解决资源的统一分发,将对象的创建分离开。策略模式解决的是策略的切换和扩展。

    ==重点:==如果在适用策略模式的地方使用简单工厂模式,如果新增加策略就要修改工厂类,而这个可能会导致其他错误,也破坏了开闭原则。如果使用了策略模式,只要将新增加的策略当作参数传递到Context类中即可。

    //3、工厂方法模式的结构、优点

    于是工厂方法模式来了,直接看结构图:

    在这里插入图片描述

    ​ 工厂方法模式结构图

    根据上图可以看出,工厂方法模式多了具体运算类的工厂类,所以当我们要新增运算,我们的步骤是:

    1. 编写具体类实现运算类重写运算方法.
    2. 实现自己的工厂类实现抽象工厂类。
    3. 根据需要,在客户端选择合适的工厂创建不同的运算对象。

    所以工厂方法模式解决了简单工厂破坏了开闭原则的缺点。

    九、原型模式(创建型)

    用原型实例指定创建对象的种类,并通过这些原型实例创建新的对象。

    一句话就是通过clone()克隆在特定场景下代替new创建对象。

    • 不需要知道任何细节的情况下,从一个对象创建另一个可定制对象。
    • new消耗的代价大。

    值得注意的是有深拷贝浅拷贝

    十、模板方法模式(行为型)

    定义一个操作中的算法的框架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义改算法的某些步骤。

    当子类出现相同的逻辑,我们可以将这些不变的逻辑提取放在超类中去实现。通过父类中定义抽象方法,在不同子类之间实现这个方法来实现可变的逻辑。

    十一、迪米特法则

    也叫最少知识原则,如果两个类不必直接通信,那这两个类不应当发生直接的相互作用。如果其中一个类需要调用另一个类的某一个方法的话,可以通过第三者转发这个调用。

    每一个类都应当尽量降低成员的访问权限,迪米特法则强调类之间的松耦合,类之间的耦合越弱越容易被复用。

    十二、外观模式(结构型)

    为子系统的一组接口(股票)提供一个一致的界面(基金),此模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。

    也就是定义一个中间类(外观),对所要使用的类进行封装,之后可以直接调用这个中间类(外观)就行了。

    外观模式体现了依赖倒转、迪米特法则

    结构图:

    在这里插入图片描述

    何时使用外观模式:

    • 首先,设计初期,有意识地将不同的两个层分离,层与层之间建立外观类
    • 其次,在开发阶段,子系统可能越来越复杂,增加外观类,减少他们的依赖。
    • 第三、维护遗留的大型系统时(很难维护、扩展),可以新增外观类对老系统进行屏蔽,之后新系统可以基于外观类进行开发。

    十二、建造者模式(建造型)

    //1、建造者模式是什么?

    建造者模式:将一个复杂对象的构建(人的构建:头、胳膊、腿)和它的表示分离,使得同样的构建过程可以创建不同的表示。

    //2、为什么要有建造者模式

    用了建造者模式后,用户只需指定需要建造的类型就可以得到它们,而具体的建造过程和细节就不需要知道。

    //3、建造者模式的结构
    在这里插入图片描述

    • Builder : 建造者的抽象类
    • ConcreateBuilder : 实现Builder接口,具备创建能力,时具体的建造者。
    • Product:具体的实体(人)
    • Director: 指挥者,根据用户的需求构建Product,传入不同的Builder对象,建造不同的实体

    //4、什么时候需要使用建造者模式呢?

    主要用于创建一些复杂的对象,这些对象内部构建间的建造顺序通常是稳定的,但对象内部的构建通常面临着复杂的变化。

    十三、观察者模式(行为型)

    //1、特点

    又叫发布-订阅模式。定义了一种一对多的依赖关系(同事依赖前台小姐姐盯住老板),让多个观察者对象同时监听某一个主题对象。这个主题对象状态变化时,会通知所有观察者模对象,是他们能够自动更新自己。

    //2、结构

    在这里插入图片描述

    //3、使用

    应用场景:当一个对象的改变需要同时改变其他对象的时候,而且不需要知道有多少对象待改变时。

    观察者模式所做的工作就是在解除耦合,让耦合的双方都依赖于抽象,而不是依赖具体,从而使得各自的变化都不会影响另一边的变化。

    但是:抽象通知者还是依赖抽象观察类,并且不同的观察者针对通知者的变化可能有不同的行为,所以可以去掉抽象观察类,使用委托方法来优化。

    • 委托可以搭载多个方法,我们可以像调用方法一样调用委托,所有的方法都会被唤起,这些方法不需要属于同一个类。但是所搭载的方法必须具有相同的原型(形参、返回值)

    十四、抽象工厂模式(建造型 )

    抽象工厂模式和工厂方法模式很类似,提供一系列相关或相互依赖对象的接口,而无需指定他们具体的类。

    具体的工厂类中有多个产品的createxxx()方法(Iuser、Idepart)。

    优缺点:

    优点:

    • 将具体的创建实例过程与客户端分离,客户端是通过他们的抽象接口操纵实例,产品的具体类名也被具体工厂的实现分离。

    缺点:

    • 当需要添加新需求的时候(product),需要新增三个类(IProduct、具体的实现),以及修改工厂类。

    在这里插入图片描述

    改进:

    去除工厂类,新增DataAccess类,根据String db = ‘’xxx‘’,通过case “不同数据库” 创建不同的实例。

    但是我们可以使用反射去优化,并通过配置文件,将使用的具体的数据库赋值给db。

    十五、状态模式

    当控制一个对象状态装换的条件表达式过于复杂时,把状态的判断逻辑转移到表示不同状态的一系列类中。通过把各种状态转换逻辑分布到State的子类之间,来减少相互之间的依赖。

    何时使用状态模式:

    当一个对象的行为取决于他的状态,并且他必须在运行时刻根据状态改变它的行为时。

    十六、适配器模式

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

    分为类适配器对象适配器

    类适配器UML结构图:

    在这里插入图片描述

    通过继承原有类、实现目标接口来实现适配。

    对象适配器UML结构图:

    在这里插入图片描述

    继承原有类、关联目标对象(类似代理)。

  • 相关阅读:
    Android Compose Column列表 数据更新列表不刷新的问题
    闪客网盘系统源码,已测试对接腾讯COS及本地和支付(支持限速+按时收费+文件分享+可对接易支付)- 修复版
    Python使用turtle绘图:pos();undo();home()
    SBT 运行出现 module java.base does not “opens java.lang“ to unnamed module错误
    谷粒商城 (二十三) --------- 商品服务 API 属性分组 ④ 品牌分类关联与级联更新
    课题学习(三)----倾角和方位角的动态测量方法(基于陀螺仪的测量系统)
    Tars-Cpp 协程实现分析
    64. 最小路径和-动态规划算法
    手把手教你Nginx常用模块详解之ngx_http_api_module(三)
    使用jdk自带的VisualVM分析hprof堆转储文件
  • 原文地址:https://blog.csdn.net/qq_50876039/article/details/127938262