LSP交流群,lsp微信交流群,LSP公众号 硬核推荐

 2022-11-01    527  

作者:oec2003

社会公众号:不止dotNET

在程序语言的世界里,能分成:程序语言的基础科学知识、程序语言的设计原则和设计模式,如果用武侠来做隐喻,基础科学知识是需要练的底子、设计原则是心法心法、设计模式则是各式各样的具体必杀技,因此说娴熟掌握了设计原则,就能以不变应如一。

程序语言的设计原则,他们最熟悉的是 SOLID 原则,SOLID 原则是四个常用原则的第一个字母简写,当然除 SOLID 原则,还有许多其它的原则,因此后面就分成 SOLID 原则和其它原则三大块来介绍。

SOLID 原则指的是常用的四个设计原则:

单个职能原则(SRP) 开放半封闭原则(OCP) 矩代替原则(LSP) 接口隔离原则(ISP) 依赖倒转原则(DIP)

他们平常写标识符会根据实际的业务情况建立类和方法,然后在方法中展开逻辑的撰写,SOLID 原则是告诉他们如果怎么科学合理地组织类和方法。最终使他们合作开发的程序能够满足:

可扩充 可F83E43Se 可阅读

这四个原则 Robert C. Martin 在《灵巧软件合作开发:原则、模式与课堂教学》和《构架干净之道》中都有完整的阐释,正好,这两本我都有。

单个职能原则(SRP)

在复试时当问道单个职能原则时,很多老师单厢回答,两个类或方法只做两件事,好像是对的,但也不唐博。Robert C. Martin 在《灵巧软件合作开发:原则、模式与课堂教学》得出的表述是「两个类如果多于两个发生改变的其原因」,而到了 《构架干净之道》表述变成了「任何两个软件组件如果只对某两类行为者负责」。

现在就有四种表述了:

只做两件事:是从内容的层次考虑,而不是变动的层次,两件事的那个事截叶,如果是两个复杂的系统,也会产生出DT类。准确蔡伯介,那个不算是单个职能原则; 多于两个发生改变的其原因:软件是在不断插值的,不可能将不发生改变,常常两个类在频密地展开修正,其原因是不止两个变动的其原因,因此让类多于两个发生改变的其原因,能让类更为contained,但顽固情况下,他们展开mammalian卡代纳回收,每个类可能将多于两个方法了,这也不是想要的结果; 只对某两类行为者负责:该表述除变动,更是考虑了变动的作者,变动的作者是平常提需求的人,这些人有着不同的职能和配角,按照那个层次,将不同的配角的人关注的内容分割到不同的地方,类的分割会更为科学合理。

举个范例:低标识符平台中的配置文件数学模型,有下面许多情景:

后台配置文件打开时的图形; 后台配置文件数据的搜集和储存; 后端配置文件布局的设置; 后端配置文件属性的设置; 后端配置文件中命令行属性的设置; 后端配置文件拖入命令行后根据数据数学模型的对接。

如果按照只做两件事的表述,这些情景都能放在两个类中,因为都是跟配置文件相关的两件事,随着功能的进化,配置文件相关的功能会越来越多,那个类也就会越来越庞大。

如果按照多于两个发生改变的其原因的表述,上面列举的情景会拆分成独立的类,也有可能将颗粒度更细,就容易变成过度设计了,导致复杂度变高。

最后一种,按照变动作者的层次,配置文件可以分成普通用户的后台使用管理员展开配置文件数学模型设置两种配角。按这两种配角展开拆分,如果想要让配置文件的布局设置变得更易用,需要调整标识符,就不会影响到后台用户的相关功能。

单个职能既指导他们怎么展开标识符的封装,将什么内容的标识符放到一起,又告诉他们需要识别标识符变动的作者,怎样将揉在一起的标识符展开科学合理地分解。

开放半封闭原则(OCP)

只要他们的产品在展开插值,就存在标识符的添加和修正。只要存在标识符的修正,就会带来风险,OCP 原则让他们尽量保持稳定的部分的不变,如果需要添加新的功能就使用扩充的方式展开实现。该原则的表述是:软件实体(类、组件、函数)如果对扩充开放,对修正半封闭。

在日常合作开发中,经常会有这样的情况:

两个很小的改动,预估半天就能完成,合作开发做着做着说时间不够,关联的地方太多了,最终两三天才能完成; 两个很小的改动,合作开发很快就调整完了,在验证时发现其它很多不相干的地方出现各种问题。

究其其原因,是标识符耦合性高,两个很小的标识符改动会产生连锁反应,扩充性差,OCP 原则是解决扩充性问题的。

举个范例:在低标识符产品的列表数学模型有两个关键点,数据源和展现模式,起初,数据源是数据库中的表,展示模式是普通的表格,慢慢地列表数学模型会不断地丰富:

数据源:表、视图、储存过程、API 接口等; 展现模式:表格、树、日历、时间轴等。

如果标识符都写到一起,当出现这些新增需求的时候,就需要修正原来的标识符:

添加很多的 if 判断; 在方法中添加新的参数用来展开许多情景的判断; 为了不影响上层的调用,方法的参数设置成了可空,很容易导致后续合作开发人员在调用时的误用。

使用 OCP 原则来看上面的范例,表述好数据输出的格式和接口抽象,就不用关心背后的源是什么,有任何的新的类型的添加,只需要扩充两个新的类展开相关逻辑的实现即可。

像他们熟悉的 VS Code 编辑器,只要符合接口标准,就能够合作开发出各式各样的插件,这是典型的面向扩充性的设计,符合 OCP 原则。

如果是单个职能原则的主要逻辑是封装,那开放半封闭原则的主要逻辑则是抽象(继承)和多态。

矩代替原则(LSP)

他们只要谈及面向接口编程,就会涉及到继承,继承中的子类不是随便怎么写都能,而是要遵循一定的原则,这是矩代替原则发挥作用的地方。

1988 年,Barbara Liskov 在描述如何表述子类型时写了这样一段话:

这里需要的是一种可代替性:如果对于每个类型是 S 的对象 o1 都存在两个类型为 T 的对象 o2 ,能使操作 T 类型的程序 P 在用 o2 代替 o1 时行为保持不变,他们就能将 S 称为 T 的子类型。

简单的表述是:子类型必须能够代替掉他们的基类型。

下面拿书中的正方形和长方形的范例,能很好蔡伯介明如果违反 LSP 后果会很严重。

按照他们的常识,正方形是一种特殊的长方形,因此正方形的类继承长方形的类就理所当然了:

public class Rectangle { protected int _height; protected int _width; public virtual void SetHeight(int height) { this._height = height; } public virtual void SetWidth(int width) { this._width = width; } public int Area() { return _height * _width; } } public class Square:Rectangle { private void SetSide(int side) { this._height = side; this._width = side; } public override void SetHeight(int height) { SetSide(height); } public override void SetWidth(int width) { SetSide(width); } }

按照矩代替的原则,子类要能够代替父类,因此如果要能够支持下面这种调用:

Rectangle rectangle = new Square(); rectangle.SetHeight(5); rectangle.SetWidth(4); int area = rectangle.Area(); if (area != 20) { throw new Exception("长和宽相乘和面积不相等"); } Console.WriteLine(area); Console.ReadLine();

上面的标识符,当 new 后面用子类 Square 代替了 Rectangle 后,area 的值就不是 20 了,因此是违反矩代替原则的。虽然他们直觉上感觉正方形是一种特殊的长方形,但从标识符逻辑的角度来看,正方形和长方形并不是 IS-A 的关系,而 IS-A 的关系是继承时需要遵循的规则 。

IS-A 是指当 A 是 B 的子类,就需要满足 A 是两个 B,判断 A 是不是两个 B 能根据所表现出来的行为,例如将鸟作为两个抽象,里面多于两个行为吃,那么猫、狗、鱼都能作为其子类,如果表述的行为多于飞,那么鸵鸟也不能作为其子类。因此说多于行为相同,才是符合 IS-A 关系,也就不会违反 LSP 原则。

LSP 原则用来指导继承关系中子类该如何设计,子类的设计要保证在代替父类的时候,不改变原有程序的逻辑以及不破坏原有程序的正确性。

由于篇幅的其原因,下一篇再介绍接口隔离原则(ISP)和依赖倒转原则(DIP)。希望本文对您有所帮助。

原文链接:https://zazhiba.com.cn/post/13587.html

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

https://zazhiba.com.cn/ 为 “自由随风” 唯一官方服务平台,请勿相信其他任何渠道。