设计模式1.(七大原则)

在这篇文章用于复习设计模式,有些是个人理解,如果不对多多指教
我们首先了解uml图和依赖传递的三种方式,

UML

定义:

Unified modeling language UML (统一建模语言):一套符号的规定,这些符号用于描述软件模型中的各个元素和他们之间的关系


关系强弱

组合>聚合>关联>依赖
依赖 在类中用到了对方,那么他们之间就存在依赖关系:以下都是

  • 类中用到了对方
  • 类的成员属性
  • 方法的返回类型
  • 接受的参数类型
  • 方法使用到
    箭头虚线

泛化(继承)
三角实线(指向被继承类)

实现
三角虚线(指向接口)

关联
引用对方类当作成员属性则为单向一对一,互相引用则为双向1对一,若为数组集合之类则为单向1对多
单向箭头 A(有属性B)–> B 双向 直线
类与类之间的联系,
导航性,即双向或单项
多重性,如1(表示有且仅有1个),n…(至少n),0,1(表示0个或者1个)n…m(n到m个)

  • 聚合:特殊关联,语义上强调的是部分与整体,还需在过程中创建对象
    空心菱形指向类C
    类A和类B是类C的属性,如大雁与雁群。初始可不new,可分开
  • 组合:特殊关联,整体与部分关系且生命周期一致,方便调用方法
    黑菱形
    其中某个属性初始就new,不可分开
    若聚合关系定义了级联删除也认为是组合

被聚合和组合的对象,如果没有子类最好private,如果有子类最好protected

组合和聚合的区别是:组合通常绑定了对象和被组合对象的生命周期(类创建之时就创建了实例对象,这样保证了封装性)。但聚合则更灵活,聚合可以聚合抽象类接口,它的具体实现由依赖传递实现,这样虽然可以多态创建不同实例执行不同方法,但一定程度上破坏了封装性,外部需要知道内部聚合的是什么类才能使用聚合。选择了聚合则其参数必定选择传递依赖。

抽象类和接口的区别:抽象类的方法不一定是抽象方法,而接口的方法都默认为抽象方法,实现接口必须重写其抽象方法,继承抽象类则无需重写其非抽象方法(因此我们推荐若是要获得类的属性实例或有许多相同的方法实现,则最好用抽象类,这样可以少些代码,而接口最好多用于无需在外部获得属性类且公用方法并不多的类)。所以如果抽象类去空方法实现接口的方法,就会把抽象方法转为普通方法,再创建抽象类的子类时则无需重写所有接口方法,只需重写自己所想要重写的接口方法即可,而这也就是适配器模式。

依赖关系传递的三种方式

接口(执行方法接收参数)
  1. (依赖)接口A 有多个方法实现
  2. 方法接口 一个执行方法接收接口A
  3. 实现类
    多个类是实现接口A
    一个类实现方法接口,1)实现执行方法。同时也会调用了接口A实现类的变量调用方法,传入实现的接口A类的变量
setter方式
  1. 接口A 同上
  2. 方法接口 一个无参执行 一个setxx方法方法接收了接口A
  3. 实现类
    多个类实现接口A
    一个类实现方法接口,同时1)实现执行方法和2)setxx方法(因为set要传入参数所以改类也3)定义一个接口A的变量),同时set方法也调用了接口A设置了成员变量,执行方法中执行成员变量(接口A变量)调用方法
    先用setxx方法传入实现接口A类的变量,之后才能执行方法
    实现类的变量调用方法
构造方法
  1. 接口A 同上
  2. 方法接口 一个执行方法
    不调用
  3. 实现类
    多个类实现接口A
    一个类实现方法接口,1)定义接口A变量2)构造器调用接口A3)执行方法
    实现类的变量创建构造传参,之后才能执行方法

是对软件设计中普遍存在(反复出现)的各种问题,所提出的解决方案
目的:代码重用性 可读性(规范性)可扩展性(可维护)可靠性(增加新功能对原有功能无影响)高内聚,低耦合

  1. 设计模式是程序员在面对同类软件工程设计问题所总结出来的有用的经验,模式不是代码,而是某类问题的通用解决方案,设计模式(Design pattern)代表了最佳的实践。这些解决方案是众多软件开发人员经过相当长的一段时间的试验和错误总结出来的。

  2. <<设计模式>> 是经典的书,作者是 Erich Gamma、Richard Helm、Ralph Johnson 和 John Vlissides Design(俗称 “四人组 GOF”)
    注意:不同的书籍上对分类和名称略有差别


七大原则

红色为目的,其没有蓝色则同时也是实现
蓝色实现

  1. 单一职责
    • 降低类的复杂度, 一个类只负责一项原则
    • 提高类的可读性,可维护性,降低变更引起的风险
    • 只有逻辑足够简单(类中方法数量足够少),才可以在代码级违反单一职责原则;
      例子:交通工具跑 =》解决1.分为 飞行的和地面的类一飞一跑(但类的改动大,且需修改客户端)
      =》解决2.交通类执行两个不同的方法(虽然类未遵守,但方法级别遵守)
  2. 接口隔离
    • 不依赖不需要的接口,一个类对另一个类的依赖应该建立在最小的接口上
    • 将大接口拆分为独立的几个接口
      例子:类A通过接口B依赖类C,但接口B有A不需要实现的方法=》把接口拆分
  3. 里氏替换
    • 在使用继承时,继承让两个类耦合性增强了,在子类中尽量不要重写父类的方法
    • 类 T1 的对象 o1,类 T2 的对象 o2。T1 定义的程序 P , o1 替换为 o2 时(也即是父类引用子类对象,而方法并没改变,这里的并非多态的意思,而是想说可能要执行一个父类的方法怕它被子类重写了), P 的行为没有发生变化,那么类型 T2 是类型 T1 的子类型。所有引用基类的地方必须能透明地使用其子类的对象(无影响)。
      1. 父类中凡是已经实现好的方法,实际上是在设定规范和契约,如果子类对这些已经实现的方法任意修改,就会对整个继承体系造成破坏。
      2. 使用继承会给程序带来侵入性,程序的可移植性降低, 增加对象间的耦合性。一个父类需要修改时,必须考虑到所有的子类。
      3. 若类无意中重写了父类的方法,会造成原有功能出现错误。复用性较差。特别是运行多态比较频繁的时候
      4. 需要可以通过聚合,组合,依赖 来解决问题。
    • 有依赖关系的两个类继承一个更通俗的基类,原有的继承关系去掉,采用依赖,聚合,组合等关系代替。
      例子:类A继承类B,并重写了方法fun1,当再创建类A对象则会执行错误的方法。=》不重写方法或采用关系更弱的关系
  4. 依赖倒转
    • 中心思想面向接口编程
    • 在 java 中,抽象指的是接口或抽象类,细节就是具体的实现类
      1. 高层模块不依赖低层模块,二者都依赖抽象
      2. 抽象不应该依赖细节,细节应该依赖抽象,细节多变,抽象稳定
      3. 抽象的目的是制定好规范,而不涉及任何具体的操作
    • 具体
      1. 低层模块尽量都要有抽象类或接口,或者两者都有,程序稳定性更好.
      2. 变量的声明类型尽量是抽象类或接口,有一个缓冲层,利于程序扩展和优化
      3. 继承时遵循里氏替换原则
        例子:人接收邮件 类person一个方法接受邮件类并调用其方法(缺,若新增其他信息渠道,则要新增类和对应接收方法)=》改进1.类person接收一个抽象类(用于接受信息)并调用其方法,其他类实现该抽象类并重写方法,这样会根据类的不同执行不同的方法且客户段代码无需改变
  5. 开闭(最重要)ocp原则
    *
    1) 一个软件实体如类,模块和函数应该对扩展开放(对提供方),对修改关闭(对使用方)。用抽象构建框架,用实现扩展细节。
    2) 当软件需要变化时,尽量通过扩展软件实体的行为来实现变化,而不是通过修改已有的代码来实现变化。

    1) 编程中遵循其它原则,以及使用设计模式的目的就是遵循开闭原则。(例如使用抽象类/接口实现增加模块变得简单)
    例子:希望加类时,尽可能少的更改原有代码
  6. 迪米特法则
    • 一个对象应该对其他对象保持最少的了解,最好是直接朋友【类与类关系越密切,耦合度越大】
      • 直接的朋友:我们称出现成员变量,方法参数,方法返回值中的类为直接的朋友,而出现在局部变量中的类不是直接的朋友。也就是说,陌生的类最好不要以局部变量的形式出现在类的内部。
      1. 迪米特法则(Demeter Principle)又叫最少知道原则,即一个类对自己依赖的类知道的越少越好。也就是说,对于被依赖的类不管多么复杂,都尽量将逻辑封装在类的内部。对外除了提供的 public 方法,不对外泄露任何信息。其还有个更简单的定义,只与直接的朋友通信。
      2. 每个对象都会与其他对象有耦合关系,只要两个对象之间有耦合关系,我们就说这两个对象之间是朋友关系(同时朋友之间也应该尽量减少public方法和属性)
      3. 谨慎使用Serializable
      • 序列化的作用将对象编码成字节流(对象传输持久化,所以能为远程通信提供对象表示法),反序列化就使字节流编码重新构建对象。
      • 危害:
        • 降低灵活性:该类一旦发布,就降低了改变该类的灵活性,它的序列化形式就变成它的到处的API的一部分,必须永远支持这种序列化形式。且每个可序列化类都有serialVersionUID,若改变类者系统会自动生成一个uid,若不声名显示uid,会破坏版本之间的兼容性,运行时产生InvalidClassException
        • 降低封装性:接受默认的序列化,私有的和包级私有的实例域都将变成到处API的一部分,不符合迪米特法则
        • 降低安全性:增加bug和漏洞可能性,反序列化的过程其实类似于调用对象的构造器,但是这个过程又没有用到构造器,因此如果字节流被无意修改或被用心不测的人修改,那么服务器很可能会产生错误或者遭到攻击。
        • 降低可测试性:随着类版本的不断更替,必须满足版本兼容问题,所以发行的版本越多,测试的难度就越大。
        • 降低性能:序列化对象时,不仅会序列化当前对象本身,还会对该对象引用的其他对象也进行序列化。如果一个对象包含的成员变量是容器类等并深层引用时(对象是链表形式),此时序列化开销会很大,这时必须要采用其他一些手段处理。
    • 注意
      1. 迪米特法则的核心是降低类之间的耦合
      2. 但是注意:由于每个类都减少了不必要的依赖,因此迪米特法则只是要求降低类间(对象间)耦合关系, 并不是要求完全没有依赖关系
        例子:如果一个类变量出现在了局部变量,并且不是它的成员变量,方法参数,方法返回值,而当我们用到该类,就必然会调用改类的方法,这就意味着我们不熟悉这个类缺调用它的方法,使得类之间耦合加深。
  7. 合成复用原则
    • 原则是尽量使用合成/聚合的方式,而不是使用继承
      1. 找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化的代码混在一起。
      2. 针对接口编程,而不是针对实现编程。
      3. 为了交互对象之间的松耦合设计而努力
        例子:如果A类想要用B类的方法,如果用继承则会耦合增强,所以用聚合或组合以降低耦合。

总结:都是为了实现低耦合,易扩展,
1.难拆开则,尽量单一(类/接口), 单一/接口隔离
2.类中使用的最好是直接朋友,只用熟悉的类,耦合低 迪米特法则
3.使用继承时可能不小心重写父类的方法,即使是想要用父类的方法用继承也会增加耦合 里氏替换/合成复用
4.抽象为模板,方便扩展而不必修改原有代码 依赖倒转/开闭
个人理解:分散的零件可以任意组合成不同的功能,使用者不需要知道原理就可以使用,同时也看不出机密

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值