# 1.设计模式概述

# 1.1 什么是设计模式

设计模式是一套反复被使用的, 多数人知晓的, 经过分类编目的, 代码设计经验的总结, 它描述了在软件设计过程中一些不断反复发生的问题, 以及该问题的解决方案, 也就是说, 它是解决特定问题的一系列的套路, 是前辈们的代码设计经验的总结. 具有一定的普遍性, 可以被反复使用.

# 1.2 学习设计模式的必要性

设计模式的本质是面向对象设计原则的实际运用, 是对类的封装性, 继承性和多态性以及类的关联关系和组合关系的充分理解.

正确的使用设计模式会具有以下的优点:

  • 提高程序员的思维能力, 编程能力和设计能力.
  • 使程序设计更加的标砖化,代码编制更加的工程化, 使软件的开发效率大大提高,从而缩短软件的开发周期。
  • 使设计的代码可重用性高, 可读性强,可靠性高,灵活性好, 可维护性强.

# 1.3 设计模式的分类

  1. 创建型模式 用于描述"怎样创建对象", 它的主要特点是"将对象的创建与使用分离", 也就是解耦合, GoF四人组,在书中提供了单例,原型,工厂方法,抽象工厂,建造者等5种创建型模式.
  2. 结构型模式 用于描述如何将类或者对象按照某种布局组成更大的结构, Gof(四人组)书中提供了代理,适配器,桥接,装饰,外观,享元,组合等7种结构型模式.
  3. 行为型模式 用于描述类或者对象之间怎样相互协作工通过完成单个对象无法单独完成的任务, 以及怎样分配职责, 提供了模板方法,策略,命令,职责链,装填,观察者,中介者,迭代器,访问者,备忘录,解释器等11种行为型设计模式。

# 2.UML

# 2.1 UML概述

UML 是 统一建模语言(Unified Modeling Language,UML) 是用来设计软件的可视化建模语言,它的特点是简单,统一,图形化,能表达软件设计中的动态和静态的信息.

UML从目标系统的不同角度出发,定义了用例图,类图,对象图,状态图,活动图,时序图,协作图,构建图,部署图等9中图.

# 2.2 UML类图概述

类图是显示了模型的静态结构, 特别是模型中存在的类, 类的内部结构以及他们与其他的类之间的关系, 类图不显示暂时性的信息, 类图是面向对象建模时的主要组成部分.

# 2.3 类图的作用

  1. 在软件工程中, 类图是一种静态的结构图, 描述了系统的类的集合, 类的属性和类之间的关系, 简化了人们对系统的理解.
  2. 类图是系统分析和设计阶段的重要产物, 是系统编码和测试的重要模型.

# 2.4 类图的表示方式

img

# 对象之间的关系

  1. 依赖关系
  2. 关联关系
  3. 聚合关系
  4. 组合关系
  5. 实现关系
  6. 继承关系

# 依赖关系

依赖关系是两个类之间最基础的, 微弱的关系, 其他的关系都是依赖关系. 两个类之间产生依赖关系的方式有很多种方式: 比如一个类的方法参数是某一个类型, 那么他们就产生了依赖关系. 一个类继承了另一个类, 那么他俩产生了依赖关系. 等等.当你在代码中使用具体类的名称时,通常意味着存在依赖关系

如何降低依赖程度? 同将某个具体的类, 变成一个更加抽象的类, 或者接口, 这个类依赖于这个接口或抽象类, 而不是这个具体的类, 这样就可以降低依赖程度.

img 采用虚线箭头表示

# 关联关系

关联关系, 就是类A可以访问类B的所有的成员变量(方法和属性), 通常形式是类B是类A的一个成员变量, 或者说类A中的某个方法返回了类B, 以此能够访问类B中的所有成员. 即一个对象总是拥有访问与其交互的对象的权限, 而简单的依赖关系并不会在对象间建立永久性的联系

img

# 聚合关系

聚合关系就是 类A包含类B, 但是与关联关系的区别呢? 就是聚合关系可能是一对多,多对多, 整体对部分, 一个类A可能包含多个类B, 而对于关联关系通常是一对一的,即两个对象之间的关系.

通常在聚合关系中, 一个对象“拥有” 一组其他对象, 并扮演着容器或集合的角色。 组件可以独立于容器存在, 也可以同时连接多个容器

img 聚合关系一端是空心的菱形, 另一端是指向被包含的组件的箭头.

# 组合关系

组合关系是聚合关系的更深的依赖关系,是一种特殊类型的聚合, 类A不仅包含着一个或者多个类B, 组件仅能作为容器的一部分存在. 也就是组件不能单独的工作, 它的存在就是依靠着类A去工作.

img 组合关系的一端是实心的菱形, 另一端是被包含组件的箭头.

# 实现关系.

通常是一个类与接口之间的关系. 类实现了这个接口 img

实现关系采用空心的三角形和虚线.

# 继承关系

img 空心三角形和实线.

# SOLID原则

  1. S(ingle) 单一职责原则:

    TIP

    如果一个类负责的东西太多, 就会导致很多原因都会引起修改, 随着程序的变大, 程序员可能找到需要的东西会很慢(大脑堆栈过载), 感觉对代码失去控制 比如说: 打印日志的功能 和实际业务的功能应该分开.

  2. O(pen/close) 开闭原则: 对扩展开放, 对修改关闭. 比如说, 类中某个方法, 有多个分支判断, 后期每多一种类型, 都需要一个分支时, 这个时候就违背了开闭原则, 因为没有对修改关闭

    TIP

    通过应用策略设计模式, 将分支判断的条件, 抽象成一个接口, 不同的类型传入不同接口的实现类, 去调用接口的方法.

    img
  3. L 里氏替换: 当扩这一个类时, 能够不修改客户端的代码情况下, 将子类对象作为父类对象进行传递. 即父类能够正常工作的场景, 其子类应该也能够正常工作. 在重写父类方法时, 不应该删除父类原有功能, 而是在它的基础上进行扩展. 这决定了子类修改后父类代码后, 能否作为父类正常工作. 需要注意6点:
    1. 子类方法的参数类型必须与其超类的参数类型相匹配或更加抽象. ::: 即不能将父类方法的参数,变得更加的具体, 父类能传递一个动物Animal, 而你将这个范围限制小了, 只能传递一个Cat.

      在Java这种静态的语言中, 重写父类方法时就将方法参数类型固定了, 所以不会出现这种问题. :::

    2. 子类方法的返回值类型必须与超类方法的返回值类型或是其子类别相匹配 ::: 与上一条相反的原则, 也就是说返回时, 只能返回与父类方法返回值类型, 同等抽象或者更加具体的类型, 因为如果更加的抽象的话, 父类后面根据返回类型处理的逻辑, 对于它是不受用的. :::

    3. 子类的方法不应该抛出, 父类没有捕获的其他异常.

      TIP

      这会导致父类的捕获异常被穿透, catch的处理不到这个异常

      对于绝大部分现代编程语言, 特别是静态类型的编程语言(Java 和C# 等等), 这些规则已内置于其中。 如果违反了这些规则, 你将无法对程序进行编译。

    4. 子类不应该强化前置条件: ::: 父类不仅仅能处理 负数和正数, 但是子类重写后只能处理正数了, 负值后抛出异常. 客户端代码之前将负数传递给该方法时程序能够正常运行, 但现在使用子类的对象时会使程序出错。 :::

# 设计模式

  1. 什么是设计模式: 针对开发中普遍遇到的问题的一种总结出来的解决方案

  2. 为什么和如何学习设计模式. 与团队高效沟通的语言,只要提到某种设计模式, 就知道当前遇到什么类型的问题.理解了对方的想法.

    在查看源码时, 为了更加了理解设计者的思想, 需要学习设计模式.

  3. 优秀的设计的特征

    • 代码复用
    • 扩展性
    • 封装变化的内容: 找到代码中后期会变化的功能(区分变化的与不变的), 然后将变化的分离出来(一个类, 或者一个方法.)
    • 面向接口开发,而不是具体的类
    • 组合优于继承: 汽车为了想要使用引擎类相关的东西时, 它不应该继承引擎, 而是应该一个引擎, 使用组合关系, 来代替继承关系.

    TIP

     因为继承是耦合度最高的依赖关系.
     1. 子类需要实现父类的所有接口
     2. 子类重写方法还得保证与基类版本兼容
     3. 继承之后具有基类内容的访问权限,
     4. 破坏了封装, 子类与超类紧密耦合.
     5. 通过继承复用代码可能导致平行继承体系的产生(出现多个维度.)
    

# 观察者模式

观察者模式定义了对象之间一对多的依赖, 当一个对象发生改变时, 它的依赖者都会收到通知并且自动更新.

  1. 首先需要定义一个被观察的对象的接口,比如这里叫做Subject
/**
 * 观察着模式的主题
 */
public interface Subject {
    /**
     * 注册一个观察者
     */
    void registerObserver(Observer observer);

    /**
     * 删除一个观察者
     * @param observer
     */
    void removeObserver(Observer observer);

    /**
     * 通知所有的观察者
     */

    void notifyObserver();
}

  1. 定义一个观察者接口

    /**
     * 观察者接口
     */
    public interface Observer {
    
        public void update(String msg);
    }
    
  2. Subject的实现类

    /**
    * 一个主题, 当xxx更新时, 需要通知所有的观察者
    */
    public class SubjectForUpdate implements Subject{
       private String msg;
       private final List<Observer> observerList = new ArrayList<>();
    
       /**
        * 主题更新方法
        * @param msg
        */
       public void setMsg(String msg) {
           this.msg = msg;
           //通知所有观察者
           notifyObserver();
       }
    
       /**
        * 注册一个观察者
        *
        * @param observer
        */
       @Override
       public void registerObserver(Observer observer) {
           observerList.add(observer);
       }
    
       /**
        * 删除一个观察者
        *
        * @param observer
        */
       @Override
       public void removeObserver(Observer observer) {
           int index = observerList.indexOf(observer);
           if(index >= 0){
               observerList.remove(observer);
           }
       }
    
       /**
        * 通知所有的观察者
        */
       @Override
       public void notifyObserver() {
           for (Observer observer : observerList) {
               observer.update(msg);
           }
       }
    }
    
  3. 观察者实现类

    public class Observer1 implements Observer{
         @Override
         public void update(String msg) {
             System.out.println("update主题更新了, 新的内容是:" + msg);
         }
     }
    
  4. 测试类

    /**
     * 测试我们的观察者模式
     */
    public class Test {
        public static void main(String[] args) {
            //1.创建一个主题
            SubjectForUpdate subjectForUpdate = new SubjectForUpdate();
            //2. 创建两个观察者
            Observer observer1 = new Observer1();
            Observer observer2 = new Observer1();
            //3. 将两个观察者, 添加到主题
            subjectForUpdate.registerObserver(observer1);
            subjectForUpdate.registerObserver(observer2);
            //4. 更新主题
            subjectForUpdate.setMsg("123");
        }
    }
    

实际上jdk也提供了观察者模式的相关接口和类, 比如主题可以继承Observed类, 即可以被观察的. 而观察者可以实现Observer接口.

在主题状态改变后, 需要调用setChanged(),方法然后调用notifyObserver()方法.

#

更新时间: 2024年5月26日星期日下午4点47分