设计模式
设计模式相当于是一种抽象的思路,为解决问题而生的固定化的套路思路。
面向对象
抽象:将具体事物抽象
封装:隐藏接口实现
继承:提取相同部分
多态:用相同的代码调用不同但类似的对象
对象之间的关系
- 依赖:类A中存在类B相关的东西
- 关联:类A中有类B
- 聚合:类A与类B是包含的关系但是非单一
- 组合:类A完全由类B等类组成
- 实现:类A实现接口类B
- 继承:类A继承B的接口与实现,并可以拓展
面向对象最重要的两个设计要点就是:
- 复用代码
- 扩展性
而一个好的设计最重要的事:不要依赖于具体类或它的实现,而是要依赖于一个接口。
这个接口可以由对方来实现,也可以通过一个抽象类(接口类)作为中间层,而这个接口类表示我们所想要依赖的最本质的特性,如吃的依赖是食物等等。
另外,抽象类不要特化,特化交给继承来做,来实现接口。
且,有时组合是由于继承的。继承在枚举其下属属性的所有可能性,而组合就是将乘法变为加法,将无关的属性抽离出原本的类,作为一个新的类来实现,再通过对各自属性的继承来实现总体属性的枚举。
SOLID
单一职责原则
将一个复杂职责的类切分为多个单职责的类,使得对于每一个类仅需关注其本身而对于整体的组合也无需关注细节实现。这一点并不仅仅是为了模块化,还是为了方便我们更加掌控代码,调整整体逻辑。
但一般在实现上就是将实现归类,放在更关联的类中。
开闭原则
闭:不修改类本身与实现。保证原本的依赖类的对象仍能正常工作。
开:通过继承的方式扩展其功能或进行修改,如需要重构某一个类,通过一个子类继承后重写对应方法即可。
不要动屎山,而是在屎山上添砖加瓦。
里氏替换原则
在扩展类,也就是在进行子类继承时,必须保证子类对象能够作为父类对象而进行使用。
在对于其他的要求上,子类必须相同或更加宽泛(在函数参数与依赖对象的要求上)。
在他类对自己的要求上,子类必须相同或更加狭窄(在函数返回值与抛出异常和对他类的保证上)。
接口隔离原则
当继承时,不希望继承许多无用的声明,可以将其中一起的一部分抽象为一个接口类。而对于接口类,也可以通过组合的方式将其拆分为多个更小的接口类来防止无用的继承实现。
可以使用多重继承来实现多个接口类。
依赖倒置原则
当高层次的类直接依赖于低层次的类时,但我们不希望高层次的类关注低层次的实现,应该将低层次的类抽象为一个抽象类,使高层次的类依赖于它(同样高层次),但此时低层次的类就使依赖***(?)***于该抽象类了。
创建型模式
提供代码灵活性和复用性的创建对象的模式。
工厂模式
将多种具体产品抽象为一个抽象的产品,并以一个抽象类“工厂”来创建产品,返回一个抽象产品指针,其通过继承来实现对于不同产品的创建。
优点:
这样可以使创建与对象本身相分离,使得更加具有扩展性。
不用构造函数而使用其他的函数简介创建对象能够实现更加复杂的功能,如使其变为单例。
抽象工厂
抽象工厂实际是工厂模式的上位。工厂模式中是只有一个工厂,而抽象工厂将工厂也抽象出来,很多具体工厂继承抽象工厂,返回对应产品的抽象产品。不同的工厂生产的产品抽象为不同的抽象产品。
一般用于二维的种类情况下,多个不同系列的相关产品。
如果你有一个基于一组抽象方法的类,且其主要功能因此变得不明确,那么在这种情况下可以考虑使用抽象工厂模式。
生成器
当一个类的种类维度很大的时候,若是通过工厂的方式,需要建立很多子类,使代码变复杂。这个时候就应该使用生成器。
生成器拥有一个建造者抽象类来负责具体某一维度的创建(通过多个建造者可以来实现不同情景下的类似的结构,如windows和mac),另外一个主管类来负责决定到底让建造者抽象类来创建哪一部分。最终通过主管类来获取最终产品。
实现上,一般使用一个容器来装维度。另外,每一个建造者类需要创建一个获取最终产品的函数,这是由于产品可能接口不同,因此不能再基类中使用抽象接口返回。另外,在创建交付完后,需要重置建造者的产品以为下一次建造做准备。
可以用于构造组合树和其他复杂对象。
原型
由于类的有一部分成员是私有的,所以很难完全进行复制。为了能够进行复制,因此使用原型的方式。
原型实际就是通过复制一个具体的类的实例来“复制”。将需要复制的原型归于一个抽象原型类,其提供一个返回指向自身的指针。另外需要一个注册表类来对应类名和类的实例,一般使用map存储即可。这个类的实例里的属性可以是任意的初值,根据情况而定。
优点:
一般复制使用拷贝构造或拷贝赋值,但这会和具体类的实现相关联,原型可以使复制代码独立于具体类。
对于常用的一些特定的类实例,可以通过原型来去掉初始化时使用的时间,也可以使更加方便。
并可以通过继承以外的方式来处理复杂对象的不同配置。
单例
正如其名,就是永远只有一个实例。而这是构造函数所不能实现的。
使用单例不但是为了让某个对象共享,更是为其提供了全局变量不能提供的安全性(不懂是什么样的安全性)。
实现时在单例类中创建一个该单例类的静态指针,指向实际有意义的那个实例。另外通过一个新的函数来代替构造函数来间接构造。该函数来判断是调用构造函数创建实例或者返回已有实例。
若是在多线程中,需要使用线程锁来保证安全,否则可能最后因为多线程而创造了多个实例。
优点:
可以用作一个全局变量(用函数来调用获得)
下面都得重写
适配器
有时程序库提供的接口并不符合我们的希望,如数据类型不同等等,此时我们可以通过更改程序库来支持这样的一个接口。但是一般来说能不改原来已有完成的类是不好的,有的时候甚至我们并没有程序库的源代码,无法进行更改,因此,我们可以使用适配器来提供一个间接的接口。
适配器实际上很简单,就是一个类,类的构造函数传入一个要被适配的对象(调用函数库者),继承程序库,并重写接口,使接口符合我们希望的样子。
或者也可以使用多重继承,就无需传入被适配的对象。
一般用于使某个接口可以使用,或者当一个继承体系下某一些类共有一些方法可以通过适配器来实现(如何实现?)。
适配器一般用于更改已有接口使其适配各种各样的。
桥接
对于一个非常复杂的类,我们可以通过将其转化为一个多层的层级结构(每一个节点都是一个类)来简化它。
在这种情况下,下级的具体类将作为上级抽象类的一个对象而存在,这可以使上级抽象类不用管下级具体类的实现,自己直接调用接口即可。
每一层的类的修改都不影响其他类的情况,易于更改,维护。
另外由于下层类仅仅是上层的一个对象,因此可以通过传入各种不同的底层实现具体类对象来轻松更改下层实现方式。
组合
当对于当个对象和复合对象都有着很多很多类似的操作时,可以使用组合,如军队,窗口等等。
实现上:用一个类实现所有相同的操作,再分别用叶节点和组合体继承这个类。整体实现一个树的结构。