低层次架构:类之间的关系
组合 ,继承,依赖,聚集。
高层次架构:设计模式
复杂的设计模式是有简单的设计模式演化而来,解决了更复杂场景下简单设计模式解决不了的问题。
设计原则:(通过设计模式实现设计原则)
单一职责原则(SRP),从职责来理解单一的概念是模糊的,因为一件事情的职责总能无限细分,所以需要找到职责的单一标准是什么?
从SPR的原话来理解:There should never be more than one reason for a class to change.
对这句话有不同的解释,看你的英文水平及对这句话的理解程度。
我的解释是:当我们因为外在要求的原因,需要修改类的时候,我们考察这个原因,是不是有且仅有一个(有且仅有一个因素产生变化,至于这个因素有多少状态不去管它)。从数量上看,有多少因素产生变化,我们就有多少类(或接口)。
当一段代码被重复使用的时候,必然没有体现单一职责原则,要考虑代码的优化。使用类间的组合关系。
里氏替换原则:父类出现的地方,子类一定能够出现,并且不会产生任何错误或异常
为了做到这一点,在使用继承时,需要满足如下要求:
1.覆盖或实现父类的方法时输入参数可以被放大,不能被缩小。
子类在实现父类的方法时有两种途径一种是覆写,一种是重载。
前者子类方法的输入参数与父类方法的输入参数相同,后者子类方法的输入参数与父类方法的输入参数不同,或大或小(大是指子类方法的输入参数是父类方法输入参数的父类,大是指子类方法的输入参数是父类方法输入参数的子类)。
输入参数相同的情况自然不用讨论,现在讨论输入参数不同的情况。
1.当子类方法的输入参数是父类方法输入参数的子类。
此时会出现一种错误,子类在没有覆写父类方法的前提下,子类的方法被执行了。此时的逻辑是混乱的。
2.当子类方法的输入参数是父类方法输入参数的父类。
当调用子类的方法时,调用的是父类的方法。
解释这么复杂可以用一种简单的约束来表达
1.子类可以实现父类的抽象方法,但不能覆盖父类的非抽象方法。
2.子类中可以增加自己特有的方法。
3.当子类的方法重载父类的方法时,方法的前置条件(即方法的形参)要比父类方法的输入参数更宽松。
4.当子类的方法实现父类的抽象方法时,方法的后置条件(即方法的返回值)要比父类更严格。
依赖倒置原则:
抽象不应该依赖细节,面向接口编程。
面向接口的编程思想:接口的作用是什么,定义一类事物所具有的共性,也就是说,只要两个类有共同的部分,就应该分离出一个接口出来。提炼事物的共同点,然后封装之,这是设计模式的核心之一。
接口隔离原则:
接口隔离原则跟之前的单一职责原则很相似,但有不同。其一,单一职责原则原注重的是职责;而接口隔离原则注重对接口依赖的隔离。其二,单一职责原则主要是约束类,其次才是接口和方法,它针对的是程序中的实现和细节;而接口隔离原则主要约束接口接口,主要针对抽象,针对程序整体框架的构建。
采用接口隔离原则对接口进行约束时,要注意以下几点:
1.接口尽量小,但是要有限度。对接口进行细化可以提高程序设计灵活性是不挣的事实,但是如果过小,则会造成接口数量过多,使设计复杂化。所以一定要适度。
2.为依赖接口的类定制服务,只暴露给调用的类它需要的方法,它不需要的方法则隐藏起来。
3.只有专注地为一个模块提供定制服务,才能建立最小的依赖关系。
4.提高内聚,减少对外交互。使接口用最少的方法去完成最多的事情。
迪米特法则:
通俗地讲,一个类应该对自己需要耦合或调用的类知道得最少,你(被耦合或调用的类)的内部是如何复杂都和我没关系,那是你的事情,我就知道你提供的public方法,我就调用这么多,其他的一概不关心。
只和朋友交流:朋友类的定义是这样的:出现在成员变量、方法的输入输出参数中的类称为成员朋友类,而出现在方法体内部的类不属于朋友类。
开闭原则:对扩展开放,对修改关闭。
每种设计模式必然会体现一种或多种设计原则,或处理具体的应用场景。
设计模式:
建造类模式:
替代new这种最简单的创建对象的方法。
单例模式:
特点:一个类只能生成一个对象
应该场景:
设计思想:将构造函数定义为私有,外部不能创建对象,只可以通过静态函数自己创建对象。
当一个类中没有属性值,只是用来向外提供一个方法是,通常应使用单例模式,同时也可以使用静态类和静态方法,但是静态类不是面向对象的,不能被继承和扩展,具有一定局限性。
原型模式:
特点:不通过new关键字来产生一个对象, 而是通过对象复制来实现。
设计思想:原型模式先产生出一个包含大量共有信息的类, 然后可以拷贝出副本, 修正细节信息,建立了一个完整的个性对象。
工厂模式:
特点:通过类名作为输入参数实现同属性不同类的创建,返回统一的接口,降低模块间的耦合,提高程序的扩展性。再次增加一个同属性不同类的对象的时候,只需增加一个类,工厂类不需做任何修改.
抽象工厂模式:
特点:与工厂模式相比,有下不同
1.通过成员方法来实现不同类的创建。
2.抽象工厂类派生出多个具体工厂类。
模板方法模式:
模板方法是一系列基本方法的组合
特点:把认为是不变部分的算法(一系列基本方法的调度)封装到父类进行实现,提高代码的重用性。一般模板方法都加上final关键字,不允许被覆写。
建造者模式:
代理模式:
《设计模式之禅》中通过游戏代练的例子形象地比喻了代理模式的工作模式,但是不能很好地解释代理模式的意图,如果我非要请代练来玩游戏,那么我还不如不玩游戏。
代理模式的定义:为其他对象提供一种代理以控制对这个对象的访问。
定义很好理解,关键的问题在于为什么要控制对这个对象的访问。
所以这个游戏的核心目的不在于为某一个游戏玩家提供代理,而是由于存在这样一个代理,当具体主题角色发生变化的时候,可以以最小的代码修改量来完成功能。而代理类不需要发生任何改变。
中介者模式:实现迪米特法则
特点:用一个中介对象封装一系列的对象交互, 中介者使各对象不需要显示地相互作用, 从而使其耦合松散, 而且可以独立地改变它们之间的交互。
责任链模式:
在多个对象都有机会处理请求的情况下,使用责任链模式可以很好的决定由谁处理这个请求。
设计思想:设定处理者的优先级,在该优先级上根据请求者身份的不同实现责任的具体分配以及传递。
策略模式:(迪米特法则应用)
借用代理模式的思路,不同的是策略模式的封装角色和被封装的策略使用的不是同一个接口。使用类对象作为封装的输入参数。
在工具类非常多的工程中,应用广泛,可以为工具的使用提供统一接口。
适配器模式:
将一个类的接口变换成客户端所期待的另一种接口,从而使原本接口不匹配而无法一起工作的两个类能在一起工作。
迭代器模式:(隐藏特性)
提供一种方法访问一个容器对象中各个元素,而又不许暴露该对象的内部细节(很少使用)
组合模式:(实现树形结构)
观察者模式:(事件—处理机制)
定义对象间一种一对多的依赖关系,使得每当一个对象改变状态,则所有依赖于他的对象都会得到通知并被自动更新。
备忘录模式:
桥梁模式:(实现依赖倒置原则)
将抽象实现解耦,使两者可以独立地变化。(抽象与具体之间的关系??? 重看)
接口是为了实现多态,抽象类是为了提高代码的重用性。
那么对于工具类来说,接口只有一种实现,接口是否还有存在的必要?
在使用工具类的时候,各个工具之间是独立的,并没有相互耦合,但是不同的工具之间有相同的零件,这种零件可以被单独分离出来。
比如在图像处理中,使用联通域降噪和联通域提取都会用到联通域分析的部分,可以将之提取出来,成为一个单独的类。
在使用工具类的时候,我们在意的是工具类中的方法,而不是工具本身,在这种情况下爱,静态方法大有用武之地。这时候不需要构造函数,也不需要对象。同时也可以使用单例模式。
数据类:
数据类集成了数据的属性,以及对数据的操作方法,数据的行为。
结构类:
完成不同类的组合。
什么时候调用父类或接口,什么时候调用子类或实现类?
我观察设计模式的程序,有的时候调用父类或接口,这样将抽象与实现隔离,实现很好的扩展性,调用子类,则可以实现子类的个性。
在new的时候,调用的是具体类的构造函数,返回可以是具体对象,也可以是抽象对象,抽象对象作为其他类方法的输入参数可以实现很好的兼容性。
返回具体对象的话,则可以调用具体的对象相对于父类对象的个性方法,也就是或只有在需要实现个性的时候,才会调用具体对象。
类的封装性
低层模块的职责不应由高层模块来完成,否则将会破坏类的封装性。
程序中类的封装性被破坏的具体表现是:
1.重复定义类的对象。
2.各种类对象交叉使用,正确的用法是,一个类对象使用完之后,才使用另一个类对象。
在判断类之间的耦合性或一个类的封装性的标准是以类的重用性作为判断依据的,如果这个类在以后的程序中多次用到,那么这个类的封装性必须严格要求,尽量减少与其他类的耦合。