ADT和oop学习心得

一、ADT

  1. 如何设计ADT?

设计ADT:设计规约Spec->表示不变性Rep->实现Impl
1.1 规约Spec的写法

方法注释
@param 参数说明
@return 返回值说明
@throw 抛出异常

1.2 规约Spec的强度和评价
1.2.1 规约Spec的强度:

前置条件越弱,规约强度越强;
后置条件越强,规约强度越强;
其他情况无法比较。

1.2.2 规约Spec的评价:

内聚的:一个好的规约Spec应该功能单一,易于理解;
信息丰富的:不产生歧义;
足够“强”的:使开发人员应该考虑足够多的异常情况并进行处理;
足够“弱”的:为开发人员减轻工作负担并降低开发成本。

1.3 抽象函数AF和表示不变量RI
1.3.1 抽象函数AF:

表示R(表示空间)和A(抽象空间)之间关系的一个映射;
AF:R->A;

1.3.2 表示不变量RI:

描述什么是合法的表示值;
是所有表示值的一个子集。

1.4 ADT实现Impl(表示泄露)

通过防御式拷贝,给客户端返回一个全新的对象,即使客户端修改了数据,也不会影响自己。然而大量的拷贝会占用内存空间,因此很多时候会使用不可变数据类型以节省频繁复制的代价。
2. ADT测试Test

需要注意的是,由于测试时开发者也相当于用户,因此不能直接访问ADT内部的数据域,只能调用其他方法测试待测的方法。

针对creater:构造对象后,用observer观察是否正确;
针对observer:用其他方法构造对象,调用被测observer,观察判断结果是否正确;
针对producer:produce一个新的对象,用observer观察结果是否正确。

二、OOP

  1. OOP的基本概念
    1.1 接口(Interface)

接口是一系列方法的声明,是一些方法特征的集合。

接口之间可以继承与扩展;
一个类可以实现多个接口;
一个接口可以有多个实现类。

1.2 抽象类

在类中只有一些方法声明,没有具体的方法(即未被实现,等待子类实现)。

抽象类至少有一个抽象方法

1.3 类

每个类包含数据说明和一组操作数据或传递消息的函数。

类必须实现抽象类中所有未被实现的方法。

1.4 继承(Inheritance)与重写(Override)

类与类之间是继承与被继承的关系;
类与接口是实现与被实现的关系。

1
2

重写:

参数列表必须与父类完全相同;
访问权限不能比父类中被重写的方法的访问权限更低;
声明为 final 的方法不能被重写;
构造方法不能被重写;
当需要在子类中调用父类的被重写方法时,要使用 super 关键字。

1.5 多态(Polymorphism)与重载(Overload)

多态:
  多态是同一个行为具有多种不同表现形式或形态的能力。

可以消除类型之间的耦合关系;
可替换,可扩充;
灵活性,简化性。
重载:
多个方法具有相同的名字,但具有不同的参数列表或返回值类型;
无法以返回值类型作为重载函数的区分标准;
被重载的方法可以声明新的或更广的异常。

三、 ADT和OOP中的等价性

  1. 不可变对象
    1.1 引用等价性与对象等价性

    “==”是引用等价,即两个引用指向了相同的内存空间;
    “equals()”是对象等价,即两个对象的域相同。

1.2 如何判断两个不可变对象相等

如果AF映射到同样的结果则等价。在自定义ADT中,如果要判断等价,需要重写equals()方法和hashCode()方法。
2.可变对象
2.1 观察等价性与行为等价性

观察等价性:在不改变状态的情况下,两个可变对象看起来是否一致;
行为等价性:调用对象的任何方法表现出一致的结果。
  对可变数据类型,往往倾向于实现严格的观察等价性,但有些时候可能导致bud,甚至破坏RI。对于可变数据类型,无需重写equals()f方法和hashCode()方法,直接继承自Object即可,而如果一定要判断两个可变对象看起来是否一致,最好定义一个新的方法。

3.总结

等价性应该是一种等价关系;
抽象函数AF是等价性的基础。

运维通常是软件开发过程中最困难的工作,涉及到软件开发的方方面面。而软件开发的大部分成本也来自于维护阶段。因此在设计时就需要考虑软件的可维护性,而不能仅仅依靠运维工程师。
  本文主要讲述面向关键质量目标的软件构造技术之面向可维护性的设计模式。与面向复用性的设计模式类似,面向可维护性的设计模式同样使用委托等机制实现。

1 原因

软件维护可以修复软件中的错误,改善软件的性能。
2 设计模式
2.1 工厂方法模式

解决的问题:工厂方法模式使得客户端可以不指明要创建哪个具体类的实例。通过定义一个用于创建对象的接口,让接口的子类型决定实例化哪个类,将类的实例化延迟到其子类进行实现。

工厂模式的意义是定义一个创建产品对象的工厂接口,将实际的创建工作推迟到子类中。核心工厂类不再负责产品的创建,这样核心类就成为一个抽象工厂角色,只负责具体工厂子类必须要实现的方法。使得系统在不修改具体工厂角色的情况下引入新的产品。
2.2 静态工厂方法模式

解决的问题:静态工厂方法模式是简单工厂方法模式的衍生。完全实现“开-闭”原则,实现了可扩展。更复杂的层次结构也使得它可以应用于产品结果复杂的场合。

相比于构造器创建对象,静态工厂方法可以具有指定的更有意义的名称,不必在每次调用的时候都创建新对象,还可以返回原返回类型的任意子类型。
2.3 抽象工厂模式

解决的问题:抽象工厂模式提供了一个接口用以创建一组相互依赖的对象,且不需要指明其具体实现类。

以UI为例,一个UI包含多个窗口控件,这些窗口控件的组合在不同OS实现不同。先抽象出一个抽象工厂接口,内有两个接口分别负责创建窗口和控件,为窗口和空间分别抽象出接口,并设计具体类实现这两个接口。根据不同UI,实现不同的窗口和控件的组合,即继承抽象工厂类,实现里面的具体方法。
本质上,抽象工厂模式是把多类铲平的工厂方法模式结合在一起。
3 不同设计模式异同

工厂方法模式和抽象工厂模式形式的不同在于工厂方法模式是一个方法,而抽象工厂模式是一个对象接口。因此对于工厂方法模式,它可以被子类型覆盖,从而将对象的实例化交给子类完成,或者说客户端不关注子类的实现,而是关注对象的功能,通过调用工厂方法得到相应的对象。而对于抽象工厂模式,它不产生一个实例,而是生产一类组件的工厂,即该对象中可以提供多个工厂接口,可以在不指定具体类的情况下提供关联或者独立组件。然而一个产品的组件可以唯一确定,不可更改,因此抽象工厂模式区别于直接使用多个工厂方法。相同点在于不将实例化的任务交给具体类本身的构造方法,而是交给另外的工厂接口。
  同工厂方法相同,静态工厂方法也将类的实现放到了抽象接口中,只不过是静态的,无法在子类型中覆盖。静态方法返回的是静态方法所在的实例化对象而不是当前类的实例化对象。因此PPT中的静态工厂方法本质上是使用了方法模式的变体静态工厂方法。

主要讲述面向关键质量目标的软件构造基本技术之面向复用的设计模式。

1 原因

降低成本和开发时间
  经过充分测试,非常可靠和稳定
  标准化,能在不同的应用中保持一致
2 设计模式
2.1 Adapter适配器模式

解决的问题:适配器模式解决了接口间不兼容的问题,如果我们想要复用已经存在的类,但是现有的类和我们想要实现的类接口不兼容,适配器模式通过增加一个适配器,将已有的类封装起来,从而解决接口不兼容的问题。

设配器模式有两种形式:
  1.继承:Java中提供了一种方式可以实现类与类之间的复用,但通常需要在实现之前设计继承层次结构,但不能取消父类中的方法,使用时需要小心;
  2.委托:委托是一个对象仅依赖另一个对象完成其功能的一部分的一种形式,可以说是一种低层次的共享实体之间代码和数据的一种机制。
  如果子类只需要复用父类中的一小部分方法,就可以不使用继承而是使用委托,从而避免继承大量无用的方法。因此组合通常优先于继承。委托发生在对象层面,而继承发生在类的层面。

不同的子类(经理,程序员和秘书等)都需要重写这个方法以满足不同的奖金计算模式。
思考两个问题:如果一个人的职位发生变动如何处理?如果两个子类计算奖金的方式一样如何处理?
这里核心的问题就是对员工的奖金计算方法的差别在对象的层面而非类的层面。因此可以考虑将计算奖金的方法抽象成一个接口,通过委托的机制实现。

2.2 Decorator装饰器模式

解决的问题:装饰器模式解决了大幅度修改类的问题。如果一个类具有多个不同功能,直接将不同的功能共同封装到这一个类,那么仅改变一个小功能就需要大幅度改变原有的类。装饰器模式通过对每个功能构造一个单独的类,通过委托机制利用现有的类增加到我们需要实现的类中。这时功能改变我们只需要委托另一个改变了功能的类,而不用更改其他类。
2.3 Facade外观模式

解决的问题:使客户端通过一个统一的接口来访问系统的功能,而不是通过很多小的接口访问,方便客户端的使用。
2.4 Strategy策略模式

解决的问题:策略模式使客户端可以自由选择实现某一个功能的不同方法。如果一个功能有多种不同的方法,策略模式通过为不同的实现算法构造抽象接口,用不同的算法实现不同的类,委托给相应的类来完成客户端的需求,运行时根据客户端传入的参数进行动态选择。
2.5 Iterator迭代器模式

解决的问题:迭代器模式使得客户端能够无视集合的类型遍历集合类中的ADT对象。迭代器模式通过让自己的集合类利用Iterator接口,实现自己特有的Iterator迭代器,允许客户端利用这个迭代器进行显式或隐式的迭代遍历。
主要讲述OO设计原则:SOLID。

1 单一责任原则

通俗的来说,单一责任原则,就是一个类实现的功能应该尽可能少。
  如果一个类承担的职责越多,,会引入额外的包,占据过多的资源,也会导致频繁的重新配置和部署,被复用的可能性也就越少。而且类中的职责相互耦合,当其中一个职责变化时,可能影响到其他职责的运作。

优点:
  每个类复杂性降低,实现简单;
  可复用性强,每个类实现的功能单一,可被多次复用;
  可维护性强,类与类之间耦合度低,该类出现异常不会影响其他类,容易定位错误位置并维护。

2 开放/封闭原则

意即功能可扩展,实现不可更改。
  一个模块需要能够衍生出新的功能以适应应用场景的变化,但模块的自身的代码不允许被随便修改。但通常行为的更改是发生在源代码层面的,因此我们需要利用抽象技术解决这个问题。核心思想是对抽象编程,而不对具体编程。即将共性的行为抽象成接口或者抽象类,通过构造不同的子类实现接口或继承父类去实现独有的功能。

3 Liskov替换原则

概括起来,任何基类可以出现的地方,子类一定可以出现。LSP是继承复用的基石,只有当衍生类可以替换掉基类,软件的功能不受影响,基类才能被真正复用,而衍生类也能在基类的基础上增加新的行为。即继承必须确保超类所拥有的性质在子类也成立。

具体实现要求:
  子类型可以增加方法,但不可删除父类的方法;
  子类型需要实现抽象类型中所有未实现的方法;
  子类的返回值必须与父类相同或者是父类的子类型;
  父类的参数必须与子类相同或者是子类的子类型;
  子类中重写的方法不能抛出额外的异常,且抛出的异常必须与父类相同或者是父类的子类型;
  对于继承父类中的犯法,子类中的方法必须与父类不变量强度相同或比父类有更强的不变量(更弱的前置条件或更强的后置条件)。
4 接口隔离原则

即将一个类对另一个类的依赖建立在最小的接口上,不应该强迫客户端依赖不使用的方法。还应将胖接口分解为多个小接口。
5 依赖转置原则

即高层次的模块不应该依赖于低层次的模块,而都应该依赖于抽象,抽象也不应该依赖于实现细节。要求我们对抽象进行编程,而不是对实现进行编程,这样可以降低客户与实现模块之间的耦合。

6 联系

依赖转置原则:相对于细节的多变性,抽象的东西更稳定。以抽象为基础搭建的框架比以细节为基础搭建的框架要稳定得多。在Java中就是,使用接口或抽象类制定规范和契约,将具体细节操作交给实现类。

开放/封闭原则:核心思想是用抽象搭建框架,用实现扩展细节。因为抽象的灵活性好,适用范围广,可以保持软件架构的稳定。而软件中的细节容易改变,当软件的需求发生变化时,我们只需要新建一个实现类就可以适应这个变化。

归纳起来,实现依赖转置原则的过程体现了开闭原则。例如,有一个接口A,实现类B,我们现在有一个新的需求,我们可以直接编写一个实现类C,而不是去修改A的源代码。这就体现了对修改封闭,对扩展开放。意即开放/封闭原则是总的设计纲领,是最终软件构造需要实现的效果。其他所有的原则都是基于这个原则衍生的。

————————————————
版权声明:本文为CSDN博主「Noctis_tel」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/Noctis_tel/article/details/118360291
原文链接:https://blog.csdn.net/Noctis_tel/article/details/118337532
原文链接:https://blog.csdn.net/Noctis_tel/article/details/118339090
原文链接:https://blog.csdn.net/Noctis_tel/article/details/117996626

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值