优雅のJava —— 前置知识 七大原则

前言

主要是用来当字典的 需要的时候回来看看 建议结合具体问题再食用这类教条内容

设计模式的目的

  • 重用
  • 可读
  • 可拓展
  • 鲁棒
  • 高内聚
    少公开接口 但是公开的接口一定执行完美
  • 低耦合
    减少一个模块的错误对其他模块的影响 或者说减少模块间的联结 降低错误传递的概率

七大原则

  • 单一职责原则
  • 接口隔离原则
  • 依赖倒转原则
  • 里氏替换原则
  • 开闭原则
  • 迪米特原则
  • 合成复用原则

单一职责原则

一个类只干一件事,否则当需求更改的时候,更改类的一个功能会影响其他功能

所以最好只有一个功能

接口隔离原则

如果接口中有 某些类不需要的部分,那就拆分之
否则就会过于臃肿,而且实现接口的类会非常难受,因为实现了但是用不到 不是好设计

比如原始接口包含1~5个方法,类A需要123号方法,类B需要246号方法,那么我们应该拆分接口为三个接口,

  • interface1 方法1 3
  • interface2 方法2
  • interface3 方法46

这样就可以避免多余的方法被实现出来。

但是凡事有个度 有时候太零散的接口也不是好事

相比于单一职责原则 接口隔离的不同点在于他是对抽象类的约束,而单一职责是对具体类,具体方法的约束

依赖倒转原则

面向接口编程为核心 面向抽象的 高层的设定 规范

  • 抽象的(接口)不能依赖具体实现(类 方法)
  • 高层模块不能依赖底层模块

Why
因为抽象的东西很稳定 我们一般先定大战略 大方针 大目标(接口) 再考虑具体如何实现(具体类 具体方法)
所以我们先构建框架 再向下实现 效率会很高 而不是计划多次易稿走弯路 最后失去方向变成小咸鱼

所谓“依赖”,比如你需要的形参 需要一个具体的方法,拿具体的一个方法作为形参,其实不如拿其类实例作为形参
同理,如果以 类的实例作为形参,确实直接简单,但是如果有很多相似的类,那不如以接口或者抽象类作为形参

这就是作为的 不能依赖底层模块 因为我们输入参数(形参)需要更强的泛化能力,应对更多样的输入,使功能更加强大,同时代码更加简洁。

其实对于用接口作为形参代替具体的类 这样还有个大利好在于,客户端(你可以理解为执行主函数main)里面的代码可以不用更改,我客户端传进去的还是类实例,但是我实际程序是把这个具体的类实例作为符合接口规范的类的一个实例来解读,其输入范围大了很多,功能和泛化能力强了很多,而且不影响客户端程序 意味着低耦合,而且功能强大——高内聚。

所以我们之前以具体类作为形参的,就可以换成接口,来作为新版本:)

那么除了上面说的接口方式实现依赖(简单来说就是传参) 还有别的的方式:

  • setter
  • 构造方法
  • 接口

套路差不多 异曲同工。

总结一下 除了上述的技巧 我们还需注意

  • 底层模块尽量有抽象类或者接口
  • 变量的声明类型尽量是抽象类和接口 泛化能力更强
    • 变量引用和实际对象间 存在缓冲层(也就是如果要改需求 就不会仅仅针对一个对象 而是能改造符合接口的一堆对象)

里氏替换原则

我们先来思考 继承 这个概念

一般来说 我们先具体做子类 再演绎成父类 是因为我们的诸多子类有通用的一个方法 那么直接把共同方法放到父类 这样再继承会很舒服(代码更简洁 易懂)

这样做确实没啥问题其实 但是再讲一个东西就问题很大了——重写

这样主要是为了更多的功能 重写父类代码 增加更多的功能 同时不会污染父类

但是重写是有弊端的:)

极端的例子 你继承了一个方法 然后重写了999个方法。。
那样的话不如说 你和原来的父对象同时继承一个公共的基类 基类只有那一个公共的大家都要用的方法

如果真的想功能增强 其实还可以定义接口 然后用具体类来实现功能增强 解除原来父类和子类之间耦合 变为平行关系

里氏替换的原意就是 我用子类尽量可以代替父类 言下之意就是你尽量要子类重写父类的方法 当然你可以添加自己的独特方法。

很可能你会忘记你重写了 然后下意识以为可以用 结果GG
也可能你的子类内部的方法实现是依赖这个被重写的方法的 你这样重写 可能造成混乱
毕竟同一个方法名 效果不一样 这个对于大工程简直是灾难:)

总之 继承虽好 可不要重写哟:)别的方式 比如接口 抽象类 聚合 组合 依赖等方式实现就好

开闭原则(OCP原则)

之前的原则告诉我们 用抽象构建框架 再实现拓展细节

在这个基础上我们看看开闭原则

定义是 对被调用者 也就是功能的提供者 我们应该开放拓展的方式 而对于调用者 也就是被服务方,我们关闭拓展的方式。

说白了
当需求变化 调用者当然最好是不用修改为好(形参设置为接口或者抽象类等等就ok了) 你想 你调库调参的时候 当然不希望你提供库的人加个功能 我所习惯的调库方式就需要大改

而是被调用者的添加(比如添加一个类 ) 而我还是照样调库。

迪米特法则

最少知道原则 一个类对自己依赖的类 知道的越少越好
也就是耦合度的降低
被依赖的类无论多么复杂 都将复杂逻辑封装在类的内部 对外就提供接口就好 不透露什么

另外 迪米特法则的延伸(具体情况)就是不要使用陌生的类 所谓陌生的类 没有形参等直接的调用 完全没啥交集的

举个栗子
我自己写了个循环链表相关的节点 Node类
现在我写的 program类要使用链表

  • 一种方式是 我在program类做一个成员函数 直接调用Node类 然后封装了增删改查功能
  • 另一种方式是
    1️⃣我再做一个类 以Node类作为构造函数的参数(或者别的方式传参 一个意思)命名为LinkedList类 在这个类中我封装了增删改查
    2️⃣之后 program类中我传入LinkedList类实例 然后调用LinkeList的成员函数 这样实现增删改查

两种方法 如果自己写个demo当然没有区别 但是如果团队合作 第一种方法的问题会很大 违反了迪米特法则 将本应该不对外部暴露的具体实现部分直接开放给客户(调用者 caller)

第二种方式就封装的会更好

其实结合前面的开闭原则 依赖倒转原则 我们知道 仅仅传入LinkedList类实例也是不妥当的 最好是设计一个链表接口 或者更广泛的 数据结构接口规范(打个比方) 然后传入接口 至于具体需要什么样的数据结构 只需要传入实现的相应类实例就好了 这样程序泛化能力 稳定性 以及debug的方便程度 还有所谓低耦合 高内聚的特点都能体现出来

没想到吧 就这么小小的改动 会对效率有这么多方面的提升:)

合成复用原则

我们先考虑一个小问题 B类想要使用A类的几个方法 应该怎么做呢?
继承(泛化 generalization)?我想这个再明显不过了吧

在这里插入图片描述

还有别的方法嘛?有的 还有两种关联(association)关系

合成composite

在这里插入图片描述

聚合aggregation

在这里插入图片描述

合成复用原则

回到合成复用原则
原则的内容很简单:尽量使用合成 聚合 而非继承依赖的方式 这样可以降低耦合度

那这么说 难道继承就没有用处了嘛?并不是 只要不太违反里氏替换原则 而且继承真的可以让代码变的好很多 比如B不仅仅是用A的方法 还有A的成员变量等很多成员属性 用了也ok 这也是种妥协权衡罢了

所以我们学到了什么?

其实七大原则的核心目的就是 正如之前开始讲的:
在这里插入图片描述
关键是松耦合

而实现的关键在于 分离不变的与变化的
具体来说 一般抽象的规则是不变的 具体的实现是变化的 所以 进一步来说就是 面向接口编程而非 面向具体的类编程

说来这个思维过程很有意思
一开始学C 我们是面向过程——一个个具体的代码
之后我们会封装成函数调用
在之后学了面向对象 我们会先做个类 再完善
到现在 业务逻辑更加复杂的情况下 我们进一步 先做接口规范 再考虑具体实现的类 再考虑具体的函数 再考虑函数具体实现

这也可能就是思维的一步步进阶之路的:)

类之间的关系

依赖 dependency

在这里插入图片描述
其实只要一个类中用到了别的类 就可以成为依赖
但是注意 根据合成复用原则 不要使用“陌生类” 也就是非 传参 setter得到的类 而是直接全局获取的

继承(泛化 generalization)

其实算是依赖关系的特例:)
在这里插入图片描述
另外**实现(implementation)**也是依赖的一种特例

关联关系(association)

注意这个是具有方向的
比如person类和IDcard类 人可以包含一个成员类IDcard 而且是一对一的 但是IDcard不包含person 这就是单向关联
那假设IDcard包含person类(持卡人),那就是双向的

在这里插入图片描述

组合composite

在这里插入图片描述

聚合aggregation

在这里插入图片描述

其实组合和聚合关系 形式上很好区分 如图
在这里插入图片描述

组合是不可或缺的 不可分离的 同生共死 比如图中 人person不能没有head 同时如果人没了 头也没了(垃圾回收掉)
但是人和他的身份证(IDcard)是可以分离的 无所谓的 人销毁了身份证也还在(打个比方)

这里的关键是 其实不是 “可否分离” 而是person删除以后 成员类是否存在的问题
假设代码写的是类似IDcard的形式 但是另外有代码是实现了级联删除的效果 也就是IDcard会跟着被销毁 那么就认为是组合关系:)

  • 4
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值