一.委派的定义
委派的定义很简单,一个对象请求另一个对象的功能罢了。
就是自己不干活,而交给别人干,自己负责调度,负责管。委派本身在我看来并不强大,强大的是委派与继承的共用。
二.委派的分类
委派中A对象调用B对象,根据B对象的来源不同,可以分为不同的种类。
依赖:B对象是动态传入A,并且A不保存B对象。B对象只是与A的方法依赖。这是临时性的委派。
关联: B作为A的成员被保存。
关联分为两类:组合和聚合。
在我的理解中B是A的组成,A与B不可分离,B消失只有A消失,A与B是组合关系,就像CPU组成电脑,电脑是不能随便更换CPU的,没了CPU电脑就不是电脑。
聚合关系:A会保存B,但A与B是可分离的。就像鼠标和电脑,电脑可以随时换鼠标。
三.继承与委派的关系
从语义上说,继承是A is B,委派是A has B.
从对被调用类的的关系说,继承是A类需要B类中所有方法,委派是A类需要使用B类部分方法。
从类与对象关系说,继承是类与类的关系,委派是对象之间的关系。
我觉得,继承对于类的限定太严格。而委派就灵活的多。继承是静态的,没有灵活性。而委派是动态的。
四.CRP原则
组合复用原则:要求应该使用组合实现复用,而不是继承。
从基类继承而来的实现是静态的,不可能在运行时发生改变,没有足够的灵活性。于是就提出了组合/聚合复用原则,也就是在实际开发设计中,尽量使用合成/聚合,不要使用类继承。即在一个新的对象里面使用一些已有的对象,使之成为新对象的一部分,新对象通过向这些对象的委派达到复用已有功能的目的。就是说要尽量的使用合成和聚合,而不是继承关系达到复用的目的。
五.面向复用的结构性设计模式
最近也在看JAVA设计模式与实践,里面是这么定义设计模式的:设计模式是前辈的经验积累,是对一般问题通用的解决方案,增强系统的可维护性与可复用性。
设计模式是面向于一般问题的通用解决,我们开发时总会有相似的场景出现,而设计模式就是解决这种场景的。设计模式的学习,必须要去体会场景,去思考抽象一般问题,学会把场景套用在一般问题上。
适配器模式
一般问题:现在存在的类提供的方法,不符合客户端调用接口。
解决方案:用适配器类适配接口。
这个问题解决遗留代码之间的复用。解决老问题。
适配器类实现接口,而接口方法中进行委派,委派给遗留类。
装饰器模式
这个模式用来动态地为对象添加新功能。
一般问题:1.实现很多不同特性的任意组合。
2.扩展已有的功能
装饰器使用
通用模块接口,具体实现类,与装饰类都实现通用接口。
而装饰器再委托对于每个方法再委托给内部的一个模块接口。
额,没有UML图好难说,举个栗子。
现在有一家咖啡店,买黑咖啡,可以加糖,加奶油,加冰。加一种算不同钱,如果只能加一种,我么可以写成不同的类。可是如果是任意组合呢?
装饰器模式就是创建一个咖啡接口,里面有一的算钱的功能,黑咖啡是实现,而奶油,糖,和冰我们也表示为咖啡接口,但内部还有有一个咖啡类。我们的算钱方法,就是调用内部的算钱方法,再添加该种配料的钱。
这样,我咖啡加糖,加完加奶油,可以随便扩展。
书上说分为透明装饰器和不透明装饰器两部分
其中透明装饰器是最能发挥装饰器优点的,也是最适合装饰器模式的。
在我看来,不透明装饰其实根本不如直接委派,不透明装饰很多缺点,所以我在实验中没有使用不透明装饰。
透明装饰器
完全的面向抽象编程,符合LSP原则。装饰器对被装饰只有方法扩展没有重写。这是最好的装饰类,因为多层装饰以后,我们还可以直接调用最外层装饰的方法。客户端无需知道我们的装饰过程。
这个最适合扩展方法功能,也是最理想的装饰。一般在JAVA源码中被用来添加缓冲层都是这个模式。
不透明装饰器
不透明装饰更灵活,但是破坏了LSP原则,多层装饰也无法统一。
装饰器重写了子类方法。这种不能完成多层装饰。我们应该尽可能实现透明装饰器。而不是不透明装饰器。
装饰器的优缺点
-
对于扩展一个对象的功能,装饰模式比继承更加灵活性,不会导致类的个数急剧增加。
-
可以通过一种动态的方式来扩展一个对象的功能,通过配置文件可以在运行时选择不同的具体装饰类,从而实现不同的行为。
-
可以对一个对象进行多次装饰,通过使用不同的具体装饰类以及这些装饰类的排列组合,可以创造出很多不同行为的组合,得到功能更为强大的对象。
-
具体构件类与具体装饰类可以独立变化,用户可以根据需要增加新的具体构件类和具体装饰类,原有类库代码无须改变,符合“开闭原则
-
缺点:动态组合功能,出问题需要排查每一个功能类。排错复杂。要求每一个类测试充分。
外观模式
问题:客户端需要调用大量方法完成功能,过于繁琐
解决:我们提供统一的接口,为客户端提供一个小接口完成需要的功能。
在我看来,这就是把客户端的职责下移到后端开发身上,提供一个小接口,便于方法调用。
六.面向复用的行为模式
策略模式.
问题:同一个方法,不同的算法实现。
很多时候不同算法对于不同数据性能不同。
解决方案:将其放入一个接口,对于该创建不同实现类。然后动态传入需要的方法类。
策略模式可以说是标准的用委派实现复用,动态实现不同的方法。运行时创建,策略模式思想就是基于委派,我自己只管方法调度,具体实现让委托类实现。
模板模式
问题:不同方法有统一的调用顺序,但是具体的方法实现不同。
我用个通俗比喻:就像人一天吃饭,不同的人都要一日三餐,而每个人吃的不同。
解决方法:设置一个抽象类,里面有一个方法有具体的调用过程,抽象的步骤。而子类重写具体步骤。
模板模式是要依赖于一个具体的方法调用过程。所以模板模式是不允许接口的(额,default方法好像也可以,我一开始实验亲自写了个模板。),是抽象类。
并且模板是完全依赖继承而与委派没有关系的。这是很不一样的一点。
迭代器模式
问题:用户希望遍历类中所有数据
解决方法:这回是现成的,实现Iterable 接口,实现里面的iterator方法返回自定义的iterator类(自己实现hasnext,next)方法。
Iterable接口是表明我有一个迭代器给你用,而具体迭代要给iterator类。
迭代器模式,虽然是具体的,但这个设计思想还是在JAVA中很普遍的,在我看来,用接口定义功能。统一不同的类。