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

开闭原则是面向对象设计的最基本设计原则,而遵守开闭原则的核心思想就是面向抽象编程。

举个例子,超市里面的商品需要出售。

1、新建一个商品接口:

package com.zwx.design.principle.ocp;

import java.math.BigDecimal;

public interface IGoods {

String getName();

BigDecimal getSalePrice();

}

2、新建一个白菜商品类:

package com.zwx.design.principle.ocp;

import java.math.BigDecimal;

public class Cabbage implements IGoods {

@Override

public String getName() {

return “上海青”;

}

@Override

public BigDecimal getSalePrice() {

return new BigDecimal(“2.98”);

}

}

这时候到了晚上了,白菜要打折清仓,只卖1.98。这时候应该怎么做?

直接改白菜商品类的getSalePrice方法可行吗?不可行,可能会影响到其他地方。

那直接改接口呢,新增一个打折方法可行吗?假如有几千种商品,我就只有白菜这一个商品需要打折呢,那么这显然也不合理,再不然就直接在白菜类里面单独新增一个打折方法,这些方法看似可行,但是都违背了开闭原则中的对修改关闭。所以我们的做法是再新建一个白菜打折类:

package com.zwx.design.principle.ocp;

import java.math.BigDecimal;

public class DiscountCabbage implements IGoods {

@Override

public String getName() {

return “上海青”;

}

@Override

public BigDecimal getSalePrice() {

return new BigDecimal(“1.98”);

}

}

这样子就符合了开发原则,扩展灵活,后面如果有其他商品需要打折可以一样处理

里氏替换原则(Liskov Substitution Principle,LSP)


里氏替换原则由麻省理工学院计算机科学实验室的芭芭拉·利斯科夫(Barbara Liskov)在 1987 年的“面向对象技术的高峰会议”(OOPSLA)上发表的一篇文章《数据抽象和层次》(Data Abstraction and Hierarchy)里提出:继承必须确保超类所拥有的性质在子类中仍然成立。也就是说如果对每一个类型为T1的对象o1都有类型为T2的对象o2,使得以T1所定义的程序P在所有的对象o1都替换成为o2时,程序P的行为没有发生改变。

在具体一点就是说如果一个软件实体适用于一个父类的话,那么一定适用于子类,所有引用了父类的地方都必须能透明的使用其子类对象。具体的可以总结为以下几条原则:

  • 1、子类可以实现父类的抽象方法,但是不能覆盖父类的非抽象方法。

  • 2、子类中可以增加自己的特有方法。

  • 3、当子类方法重载父类的方法时,方法的前置条件(即方法的输入/入参)要比父类方法输入的参数更宽松

  • 4、当子类实现父类的方法(重载/重写/实现抽象方法),方法的后置条件(即方法的输出/返回值)要比父类更严格或者相等

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

  • 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;

自我介绍一下,小编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-ELcGsUfL-1712929580892)]
[外链图片转存中…(img-cfiVYRv7-1712929580893)]
[外链图片转存中…(img-ihoCWRN4-1712929580894)]
[外链图片转存中…(img-z1h0whTf-1712929580894)]
[外链图片转存中…(img-JghbXKye-1712929580895)]

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

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

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值