设计模式:概念 & 分类 & 功能 & 原则

在这里插入图片描述
本文是对设计模式概念、分类、功能、原则的介绍, 不含有任何技术性介绍。旨在对设计模式有更清楚的认识,理解。

本文是对设计模式的概念、分类、功能、原则的总结归类。文中会引用一些简单的例子!

后续会陆续对这些设计模式逐一总结!

目录

在这里插入图片描述

设计模式的目的

设计模式是前辈大佬们的代码设计经验,是一种解决特定问题的套路。目的就是为了提高代码的重用性、可读性、可靠性、可扩展性、灵活性

学习设计模式的意义

设计模式本质

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

优点
  1. 提高程序员的编程能力、设计能力、思维能力
  2. 程序设计标准化、代码编程标准化,提高开发效率、缩短开发周期
  3. 提高代码重用性、可读性、可靠性、可扩展性、灵活性

设计模式的分类

在这里插入图片描述

设计模式功能描述

概念理解:

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

这23中设计模式,并不是孤立存在的,很多设计模式存在一定关联的
我们除了要理解这些设计模式,更重要的是我们在灵活运用起来,不能只停留在理解上

设计模式的六大原则

在这里插入图片描述

设计模式设计原则
开闭原则
  1. 定义
    通俗点说写好一个模块,有新需求产生时,再不改变该模块的源代码前提下,可以扩展模块的功能,满足新的需求。就是对外扩展,对修改关闭

  2. 作用
    能让那个该模块/该软件有一定适应性、灵活性、可扩展性、稳定性

  3. 实现方法
    两大核心点:抽象约束,封装变化
    可以通过接口 或者抽象类,为模块/软件封装一个稳定的抽象层,将相同可变的因素封装在相同的具体实现类中。后续扩展只需要派生一个新的具体实现类扩展好了。

  4. 实例:手机来电铃声的设计
    分析:用户可以根据自己的喜好设置喜欢类型的铃声,系统内置的或者下载新的音乐。
    可以定义一个抽象类(Abstract Music),每个具体铃声是其子类(specific Music),用户可以根据需要选择或者增加新的音乐,而不需要修改源代码。这种就符合开闭原则

在这里插入图片描述

里氏替换原则
  1. 定义你
    超类所拥有的性质必须在子类中仍然成立

  2. 作用
    a.是实现开闭原则的重要方式之一
    b.克服了继承父类造成的可复用性变差的缺点
    c.后续类的扩展不会给现有的系统引入新的错误,降低代码出错的可能性

  3. 实现方法
    子类可以扩展父类的功能,但不能改变父类原有的功能。即子类继承父类时,可以增加新的方法,新的功能外,尽量不要重写父类的方法

  4. 实例:“ 鸵鸟不是鸟 ”
    例如:鸽子的父类是鸟,鸵鸟的父类是鸟,现在要测它们每小时的飞行距离?
    问题来了:鸽子可以飞,鸵鸟却不会飞!如图:
    在这里插入图片描述
    如图所示的关系放到代码中时,去测试飞的功能,肯定是不合理,会报错的。在代码中,飞的功能,对于鸵鸟是压根就不存在的。这样设计是达不到我们想要的结果,所以你需要重新设计他们的关系。

根据上述我们知道:
1. 要测试飞的功能
2. 鸵鸟是不会飞的
3.里氏替换原则的核心定义:超类所拥有的性质必须在子类中仍然成立
所以我们需要找到它们的共性:奔跑的能力, 无法测鸵鸟飞的时速,可以用奔跑的时速代替。

在这里插入图片描述

依赖倒置原则
  1. 定义
    依赖倒置原则的核心思想就是面向接口编程,不能面向实现编程

  2. 作用
    a. 降低类间的耦合性,代码的可读性以及可维护性
    b. 提高系统的稳定性
    c. 减少并发引起的风险

  3. 实现方法
    利用面向接口编程降低类之间的耦合。需要满足以下规则:
    a. 每个类都要提供 接口 或抽象类,二者都有也可
    b. 变量的声明类型尽量是接口或抽象类
    c. 任何类不应该有继承类
    d. 遵循里氏替换原则

4.实例:在购物程序中的应用
分析:商店类中有个出售: sell()方法, 顾客类 通过该方法在王二(Wanger)网店购买:

Customer:顾客类

class Customer
{
    public void shopping(Wanger shop)
    {
        //购物
        System.out.println(shop.sell());
    }
}

顾客类又想从李四(LiSi)网店购买时,这里又要修改代码,才能实现:

class Customer
{
    public void shopping(LiSi shop)
    {
        //购物
        System.out.println(shop.sell());
    }
}

这样的设计极为不合理的,换一家就要该机一次源码。
这里面向接口编程,可以设计一个接口Shop提供共有的sell方法,网店去实现这个Shop接口,这样顾客类Customer可以访问任意网店,或者新增网店,都不需要修改源代码了。
在这里插入图片描述
就跟天猫一样。

单一原则
  1. 定义
    一个类应该有且仅有一个能引起它变化的原因,否则这个类应该被拆分

该原则提出:对象不应该提供太多职责,过多会导致其中一个职责的变化会引起其他职责的变化 & 当客户端只需要某一个职责时,去不得不把其他职责包含进来,造成代码的冗余。
故单一原则核心:控制类的粒度大小、将对象解耦、提高其内聚性

  1. 作用
    a.降低类的复杂度。即:一个类一个职责
    b.提高类的可读性。即:复杂性降低
    c.高系统的可维护性。即:可读性提高
    d.变更引起的风险降低。变更必不可少,但遵循单一功能原则,就会减少对其他功能影响

  2. 实现
    单一原则是最简单但又最难运用。需要设计人员发现类的不同职责并将其分离,再封装到不同的类或模块中、所以需要设计人员需要较强的分析设计能力和重构经验

如下图:
在这里插入图片描述

接口隔离原则
  1. 定义
    程序员尽量将接口拆分的更小和更具体的接口,接口只包含客户关心的方法

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

接口隔离原则和单一职责都是为了提高类的内聚性、降低它们之间的耦合性,体现了封装的思想,但两者是不同的:
a. 单一职责原则注重的是职责,而接口隔离原则注重的是对接口依赖的隔离。
b. 单一职责原则主要是约束类,它针对的是程序中的实现和细节;接口隔离原则主要约束接口,主要针对抽象和程序整体框架的构建。

  1. 作用
    接口隔离原则是为了约束接口、降低类对接口的依赖性.
    a.将臃肿的接口拆分多个粒度小的接口,减小变更带来的影响,提高灵活性和可维护性
    b.提高了系统的内聚性,减少了对外交互,降低了系统的耦合性
    c.接口的粒度大小需要合理,才能保证系统的稳定性。过大,灵活性地,过小接口数量过多
    e.减少代码冗余

  2. 实现
    在应用接口隔离规则时,可以根据一下规则衡量:
    a.接口粒度合理。一个接口只服务一个子模块
    b.接口提供定制服务。只提供类需要的方法
    c.根据具体业务,进行接口拆分。没有标准的接口拆分规则
    d.提高内聚,减少对外交互。用接口最少的方法完成最多的事情

    4.实例
    学生成绩管理程序:成绩的插入、删除、修改、计算总分、计算均分、打印成绩、査询成绩信息等功能。
    按功能划分:输入模块、统计模块和打印模块

    如下图:
    在这里插入图片描述

迪米特法则
  1. 定义
    如果两个软件实体无须直接通信,那么就不应当发生直接的相互调用,可以通过第三方转发该调用。其目的是降低类之间的耦合度,提高模块的相对独立性

  2. 作用
    a. 降低了类之间的耦合度,提高了模块的相对独立性
    b. 由于亲合度降低,从而提高了类的可复用率和系统的扩展性

3.实现
迪米特法则两大特点:
a.从依赖者的角度来说,只依赖应该依赖的对象
b.从被依赖者的角度说,只暴露应该暴露的方法

使用时注意:
a.在类的划分上,应该创建弱耦合的类
b.在类的结构设计上,尽量降低类成员的访问权限
c.在类的设计上,优先考虑将一个类设置成不变类
d.在对其他类的引用上,将引用其他对象的次数降到最低
c.不暴露类的属性成员,而应该提供相应的访问器(set 和 get 方法)
e.谨慎使用序列化(Serializable)功能

  1. 实例:明星与经纪人的关系实例
    分析:明星由于全身心投入艺术,所以许多日常事务由经纪人负责处理,如与粉丝的见面会,与媒体公司的业务洽淡等。这里的经纪人是明星的朋友,而粉丝和媒体公司是陌生人,所以适合使用迪米特法则,其类图如图 1 所示
    在这里插入图片描述
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值