2022软件构造我的学习笔记(3)

本文深入探讨了面向对象设计中的重要原则,包括Liskov替换原则(LSP)、合成复用原则(CRP)以及SOLID原则。LSP强调子类应能替换父类并保持行为一致,而CRP提倡使用组合而非继承以增强代码的可维护性。SOLID原则中的每个组件都对软件设计的可扩展性和维护性有着关键影响。此外,还讨论了白盒和黑盒框架、模块化设计的度量标准以及异常和断言的处理。最后,提到了设计模式和正则表达式在实际开发中的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.LSP原则

LSP原则:

1.子类必须完全的实现父类的方法(不能删父类的方法;子类型需要实现抽象类型中的所有未实现的方法)
2.子类可以有自己的个性(子类型可以增加方法)
子类型中重写的方法必须使用同样类型的参数或者符合co-variance的参数(此种情况Java目前按照overload处理)
3.子类型中重写的方法不能抛出额外的异常(协变)
4.覆盖和实现父类的方法时输入参数可以被放大(更弱的前置条件/逆变)
5.覆盖和实现父类的方法时输出参数可以被缩小(更强的后置条件/协变)
6.更强/保持的不变量
记住一句话:LSP直观表现为——子类型传入参数不变或更抽象,返回值不变或更具体,抛出异常更少或不变或更具体

2.协变、反协变

“协变是指能够使用与原始指定的派生类型相比,派生程度更大的类型。父类型→子类型:越来越具体specific 返回值类型:不变或变得更具体 异常的类型:也是如此。

“逆变”则是指能够使用派生程度更小的类型。父类型→子类型:越来越具体specific 参数类型:要相反的变化,要不变或越来 越抽象

要注意java中参数逆变和协变是overload而非override,这个点是出过考试题的

3.数组的子类型化

Java中 数组是协变的: 对T[]数组,可以保存类型T及其子类型的数据

Number的数组元素可以是number的子类型,integer和double

在运行时,Java知道这个数组被实例化 为Integer数组,只是通过一个Number[]引用进行访问,如果是一个正常的number数组是可以赋一个浮点的值的,但是这个数组被实例化为integer数组,会在运行时报错。 

4.泛型的子类型化和通配符

类型参数在编译后被丢弃, 在运行时不可用,list<string>和list<object>是完全无关的 两个类。

这种是不通过静态检查。

可采用通配符实现两个泛型类的协变

Lower Bounded Wildcards: 下限通配符

上限通配符,这里的 extends既可以代表类的extends,也可以代表接口的implements

List list,意味着 list可以匹配多种类型中的一种,但并不意 味着同一个list可以存放所有的这些类型, 无限定通配符和下限通配符同理。

 5.Delegation

 委派/委托:一个对象请求另一个对象的功能

如果子类只需要复用父类中的一小部分方法,可以不需 要使用继承,而是通过委派机制来实现。

一个类不需要继承另一个类的全部方法,通过委托机制调用部分方法,从而避免继承大量无用的方法

有很多的delegation的应用可以看第11章设计模式的内容

delegation有两种类型:

Dependency (A use B) ,把被委派类型传入方法,在方法中调用。临时性的delegation

Association (A has B) ,这个类中有一个被委托类,返回这个类中带有的被委托类中的方法。永久性的delegation

Composition: 更强的association,但难以变化

Aggregation: 更弱的association,可动态变化

组合是组成部分脱离后不能独立存在,聚合是脱离后可以独立存在
 

上面是聚合 ,上面的listener脱离webserver仍然是一个类Httplistener

6.CRP原则

合成复用原则(Composite Reuse Principle,CRP)又叫组合/聚合复用原则(Composition/Aggregate Reuse Principle,CARP)。它要求在软件复用时,要尽量先使用组合或者聚合等关联关系来实现,其次才考虑使用继承关系来实现。

7.白盒框架和黑盒框架

白盒框架是基于面向对象的继承机制。之所以说是白盒框架,是因为在这种框架中,父类的方法对子类而言是可见的。子类可以通过继承或重写父类的方法来实现更具体的方法。

虽然层次结构比较清晰,但是这种方式也有其局限性,父类中的方法子类一定拥有,要么继承,要么重写,不可能存在子类中不存在的方法而在父类中存在。

黑盒框架时基于委派的组合方式,是不同对象之间的组合。之所以是黑盒,是因为不用去管对象中的方法是如何实现的,只需关心对象上拥有的方法。

这种方式较白盒框架更为灵活,因为可以在运行时动态地传入不同对象,实现不同对象间的动态组合;而继承机制在静态编译时就已经确定好。

 8.可维护性的常见度量指标 、聚合度与耦合度

模块化编程:

– High cohesion within modules 高内聚

– Loose coupling between modules 低耦合

内聚和耦合原则可能是评估设计可维护性的最重要的设计原则。

评估模块化的五个标准:

 模块化设计的五项规则:

▪ Direct Mapping (直接映射)

▪ Few Interfaces (尽可能少的接口)

▪ Small Interfaces (尽可能小的接口)

▪ Explicit Interfaces (显式接口)

▪ Information Hiding (信息隐藏)

9.设计原则:SOLID

▪ (SRP) The Single Responsibility Principle 单一责任原则

一个类应该只有一个引起改变的原因。

这个原则意味着一个类只能有一个职责并且只完成为它设计的功能任务。

否则,如果我们的类承担的职责多于一个,那么我们的代码就具有高度的耦合性,并会导致它对于任何改变都很脆弱。

好处:

  • 降低耦合性。
  • 代码易于理解和维护。

当一个类有多个职责,它就更加难以被理解,扩展和修改。

更好的解决办法:

我们 为每一个职责创建不同的类。

▪ (OCP) The Open-Closed Principle 开放-封闭原则

软件实体(类,模块,方法等)应该对扩展开放,对修改封闭。

根据这一原则,一个软件实体能很容易地扩展新功能而不必修改现有的代码。

open for extension: 添加新的功能从而满足新的需求。

close for modification: 扩展新的功能行为而不需要修改现有的代码。

如果我们应用这个原则,我们会有一个可扩展的系统且在更改需求的时候更不易出错。我们可以用抽象多态来帮助我们应用这个原则。

好处:

  • 代码的可维护性和复用性。
  • 代码会更健壮。

▪ (LSP) The Liskov Substitution Principle Liskov替换原则

说过太多次了。

▪ (DIP) The Dependency Inversion Principle 依赖转置原则

高层次的模块不应该依赖于低层次的模块,它们都应该依赖于抽象。

抽象不应该依赖于细节。细节应该依赖于抽象。

依赖倒转原则的意思是一个特定的类不应该直接依赖于另外一个类,但是可以依赖于这个类的抽象(接口)。

当我们应用这个原则的时候我们能减少对特定实现的依赖性,让我们的代码复用性更高。

好处:

  • 减少耦合。
  • 代码更高的复用性。

▪ (ISP) The Interface Segregation Principle 接口聚合原则

多个专用的接口比一个通用接口好。

这个原则定义了一个类决不要实现不会用到的接口。不遵循这个原则意味着在我们在实现里会依赖很多我们并不需要的方法,但又不得不去定义。

所以,实现多个特定的接口比实现一个通用接口要好。一个接口被需要用到的类所定义,所以这个接口不应该有这个类不需要实现的其它方法。

好处:

  • 系统解耦。
  • 代码易于重构。

10.正则表达式 

我们下一个博客再详细谈谈

11.设计模式 

http://t.csdn.cn/bRwI6icon-default.png?t=M4ADhttp://t.csdn.cn/bRwI6 详见我之前的博客,里面非常详细的解释了本学期需要的设计模式。

12.异常和断言

http://t.csdn.cn/qbLubicon-default.png?t=M4ADhttp://t.csdn.cn/qbLub 详见我之前的博客,里面非常详细的介绍了面向正确性和健壮性的设计。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值