【设计模式系列24】GoF23种设计模式总结及软件设计7大原则(2)

遵循里氏替换原则有如下优点:

  • 1、可以约束继承的泛滥,也是开闭原则的一种体现

  • 2、加强了程序的健壮性,同时在变更时也做到了非常好的兼容性,提高了程序的维护性,扩展性,降低了需求变更时引入的风险。

举个栗子,我们以鸟类飞翔为例:

package com.zwx.design.principle.lsp;

public class Bird {

public void fly() {

System.out.println(“我正在天上飞”);

}

}

这时候我们有一个鹰类需要继承Bird:

package com.zwx.design.principle.lsp;

public class Eagle extends Bird {

@Override

public void fly() {

System.out.println(“我正在8000米高空飞翔”);

}

}

最后我们再看看测试类:

package com.zwx.design.principle.lsp;

import com.zwx.design.principle.isp.Dog;

public class TestLsp {

public static void main(String[] args) {

Bird bird = new Bird();

bird.fly();

//替换成子类Eagle,子类重写了父类Bird的fly方法

Eagle eagle = new Eagle();

eagle.fly();

}

}

当我们用子类替换父类的时候,因为父类的方法被重写了,所以替换之后输出结果发生了改变,这就违背了里氏替换原则。

依赖倒置原则(Dependence Inversion Principle,DIP)


依赖倒置原则是Object Mentor公司总裁罗伯特·马丁(Robert C.Martin)于1996年在C++ Report上发表的文章中提出。

依赖倒置原则指的是在设计代码结构时,高层模块不应该依赖低层模块,而是都应该依赖其抽象。抽象不应该依赖细节,细节应该依赖抽象。通过依赖倒置原则可以减少类与类之间的耦合性,提高系统的稳定性,提高代码的可读性和可维护性,而且能够降低修改程序所带来的的风险。

举个栗子:

比如说有一家超市里面一开始只卖青菜:

package com.zwx.design.principle.dip;

public class SuperMarket {

public void saleCabbage(){

System.out.println(“我有白菜可以卖”);

}

}

然后心在业务开始扩大了,要卖肉了,这时候怎么办呢,可以再加一个方法,但是这么一来底层要改,调用者也要改,不利于维护,所以应该不依赖于具体实现来编程。

进行如下改写:

新建一个商品接口:

package com.zwx.design.principle.dip;

public interface IGoods {

void sale();

}

然后新建一个白菜类:

package com.zwx.design.principle.dip;

public class Cabbage implements IGoods{

@Override

public void sale() {

System.out.println(“我有白菜卖”);

}

}

然后将超市类改写:

package com.zwx.design.principle.dip;

public class SuperMarket {

public void sale(IGoods goods){

goods.sale();

}

}

这时候超市已经面向接口了,而不面向具体(白菜),如果扩张业务,想要卖肉类,直接新增一个肉类就好了

单一职责原则(Single Responsibility Principle,SRP)


单一职责原则由罗伯特·C.马丁(Robert C. Martin)于《敏捷软件开发:原则、模式和实践》一书中提出。

单一职责原则指的是不要存在多于一个导致类变更的原因。假如我们有一个类里面有两个职责,一旦其中一个职责发生需求变更,那我们我们修改其中一个职责就有可能导致另一个职责出现问题,在这种情况应该把两个职责放在两个Class对象之中。

单一职责可以降低类的复杂度,提高类的可读性,提高系统的可维护性,也降低了变更职责引发的风险。

举个栗子:

比如说超市里面的商品需要进货然后再卖出去,这就是两件事。

新建一个超市商品类:

package com.zwx.design.principle.srp;

public class Goods {

public void action(String type){

if (“进货”.equals(type)){

System.out.println(“我要去进货了”);

}else if(“售卖”.equals(type)){

System.out.println(“我要卖商品”);

}

}

}

这时候一个方法里面有两个功能,假如业务逻辑非常复杂,那么一个功能发生变化需要修改有很大的风险导致另一个功能也发生异常。所以我们应该进行如下改写,将这两个职责拆分成两个类:

package com.zwx.design.principle.srp;

public class BuyGoods {

public void action(){

System.out.println(“我要去进货了”);

}

}

package com.zwx.design.principle.srp;

public class SaleGoods {

public void action(){

System.out.println(“我要卖商品”);

}

}

接口隔离原则(Interface Segregation Principle,ISP)


接口隔离原则是2002年由罗伯特·C.马丁提出的。接口隔离原则指的是用多个专门的接口,而不使用单一的一个总接口,客户端不应依赖它不需要的接口。

接口隔离原则符合我们所说的高内聚低耦合的设计思想,从而使得类具有很好的可读性、可扩展性和可维护性,我们在设计接口的时候应该注意以下几点:

  • 1、一个类对其他类的依赖应建立在最小的接口之上

  • 2、建立单一的接口,不建立庞大臃肿的接口

  • 3、尽量细化接口,接口中的方法应适度

我们以常见的动物的行为来举个栗子:

package com.zwx.design.principle.isp;

public interface IAnimal {

void run();

void swim();

void fly();

}

这个动物接口里面包含了三个行为接口:地上走,水里游,天上飞。但是是不是所有动物都有这三种行为呢?显然不是,比如狗肯定不能天上飞,鱼只能水里游,这样没用的行为只能空着什么都不做了:

package com.zwx.design.principle.isp;

public class Dog implements IAnimal {

@Override

public void run() {

System.out.println(“我跑的很快”);

}

@Override

public void swim() {

System.out.println(“我还会游泳”);

}

@Override

public void fly() {

}

}

而如果鱼,那就得空着两个方法什么也不能做了,这就是一个臃肿的接口设计,如果遵循接口隔离原则,那么应该这么改写:

新建三个接口,每个动作都对应一个接口:

package com.zwx.design.principle.isp;

public interface IFlyAnimal {

void fly();

}

package com.zwx.design.principle.isp;

public interface IRunAnimal {

void run();

}

package com.zwx.design.principle.isp;

public interface ISwimAnimal {

void swim();

}

这时候动物狗就可以这么写:

package com.zwx.design.principle.isp;

public class Dog implements IRunAnimal,ISwimAnimal {

@Override

public void run() {

System.out.println(“我跑的很快”);

}

@Override

public void swim() {

System.out.println(“我还会游用”);

}

}

这样就实现了接口隔离,不会具备一些无用的行为。

迪米特法则(Law of Demeter,LoD)


迪米特法则又叫作最少知道原则(Least Knowledge Principle,LKP),产生于1987年美国东北大学(Northeastern University)的一个名为迪米特(Demeter)的研究项目,由伊恩·荷兰(Ian Holland)提出。

迪米特法则是指一个对象对其他对象应该保持最少的了解,尽量降低类与类之间的耦合。

举个栗子,比如说上面的超市售卖的商品青菜,老板(Boss)想知道卖出去了多少斤:

首先新建一个青菜商品:

package com.zwx.design.principle.lod;

public class Cabbage {

public void getName(){

System.out.println(“上海青”);

}

public void saleRecord(){

System.out.println(“我今天卖出去了100斤”);

}

}

这时候普通做法可以在Boss类里面集成Cabbage类,这样就可以拿到售卖记录,但是这就违背了迪米特法则,因为老板不应该直接和商品打交道,要不然商品一多,老板哪有闲情自己一个个去查,所以一般老板可以找对应的经理获取结果。

我们再新建一个经理类:

package com.zwx.design.principle.lod;

public class Manager {

private Cabbage cabbage;

public void getCabbageSaleMoney(){

cabbage.saleRecord();

}

}

最后再新建Boss类:

package com.zwx.design.principle.lod;

public class Boss {

public void getCabbageSaleRecord(Manager manager){

manager.getCabbageSaleMoney();

}

}

可以看到Boss完全不需要和青菜打交道,找经理就好了,这就是迪米特法则,不该知道的不要知道,我只要让该知道的人知道就好了,你想知道那你就去找那个该知道的人。而实际上中介者模式就是一种典型的遵守了迪米特法则的设计模式。

合成复用原则(Composite Reuse Principle,CRP)


合成复用原则又叫组合/聚合复用原则(Composition/Aggregate Reuse Principle,CARP)。指的是在软件复用时,要尽量先使用组合(has-a)或者聚合(contains-a)等关联关系来实现,这样可以使系统更加灵活,降低类与类之间的耦合度,一个类的变化对其他类造成的影响相对较少。

继承通常也称之为白箱复用,相当于把所有的实现细节暴露给子类。组合/聚合也称之为黑箱复用,对类以外的对象是无法获取到实现细节的。

这个原则还是非常好理解的,像我们开发中经常用的依赖注入,其实就是组合,所以在这里就不再举例子了。

设计模式总结

===================================================================

学完设计模式之后,其实我们也应该知道,设计模式就是在某种场景下,针对某种问题的某种解决方法,而GoF23种设计模式源于《设计模式》一书:《Design Patterns:Elements of Resuable Object-Oriented Software》是由Erich Gamma、Richard Helm、Ralph Johnson、John Vlissides四个人一起合著的,发表于1995年。这四位作者也被称之为“四人组(Gang of Four)”,所以这本书也被称之为:四人组(或GoF)书,而这本书里面就介绍了23种设计模式,这就是我们常说的GoF23种设计模式

23种设计模式大致可以分为三大类,创建型结构型行为型

创建型设计模式


创建型设计模式顾名思义就是用来创建对象的,回忆23种设计可以很容易得出其主要有如下设计模式(加粗的表示高频使用设计模式):工厂方法模式抽象工厂模式单例模式建造者模式原型模式

| 设计模式 | 一句话总结 |

| — | — |

| 工厂方法模式 | 由子类来决定创建具体对象 |

| 抽象工厂模式 | 允许使用者创建对象的家族,无需指定具体类 |

| 单例模式 | 只为对象提供一个全局的访问点(世上只有一个地球) |

| 建造者模式 | 允许使用者自由搭配组合对象 |

| 原型模式 | 通过复制原对象来创建新对象 |

结构型设计模式


结构型模式主要包括以下模式(加粗的表示高频使用设计模式):代理模式门面模式装饰器模式享元模式组合模式适配器模式,桥接模式。

| 设计模式 | 一句话总结 |

| — | — |

| 代理模式 | 增强对象功能 |

| 门面模式 | 统一访问入口(拨开云雾见天日) |

| 装饰器模式 | 为对象添加新的功能 |

| 享元模式 | 共享资源池(节省资源,从我做起) |

| 组合模式 | 统一整体和个体的处理方式 |

| 适配器模式 | 兼容转换(我所做的一切,只是为了配得上你) |

| 桥接模式 | 将抽象与具体分离开来 |

行为型设计模式


结构型模式主要包括以下模式(加粗的表示高频使用设计模式):模板方法模式策略模式责任链模式状态模式,迭代器模式,命令模式,备忘录模式,中介者模式,解释器模式,观察者模式,访问者模式。

| 设计模式 | 一句话总结 |

| — | — |

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Go语言工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Go语言全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Golang知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加V获取:vip1024b (备注Go)
img

一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

OPPO等大厂,18年进入阿里一直到现在。**

深知大多数Go语言工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Go语言全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
[外链图片转存中…(img-aHJSDVdz-1712929607843)]
[外链图片转存中…(img-LxAZfpXp-1712929607845)]
[外链图片转存中…(img-OQXJ6g7i-1712929607845)]
[外链图片转存中…(img-c2deEJS5-1712929607846)]
[外链图片转存中…(img-PRrfAxBQ-1712929607847)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Golang知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加V获取:vip1024b (备注Go)
[外链图片转存中…(img-5mzwcHHi-1712929607847)]

一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 8
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值