本文为4.3部分(Deisin Patterns for Reuse)的笔记
设计模式更强调多个**类**、**对象**之间的**关系**和**交互**过程
结构型模式(Structural patterns)
1 适配器模式(Adapter)
适用场景
类之间的接口不兼容
思想
通过增加一个接口,将实现的子类封装起来,让client面向接口编程(相当于一根HDMI的数据线想接一个VGA的接口,那么就需要一个HDMI转VGA的转接头)
可以设计这样一个类C来“连接”两个不兼容的类A和B(B实现A),(1)C的前置条件与A需要实现的方法的前置条件完全一致,而在C的内部,(2)可以将传入的参数转化成符合B的方法的前置条件,(3)通过B来满足A的后置条件(可以继承,也可以委托delegate)。综上,可以发现B消失了,而A通过C这一转接器实现了功能(由上述三个条件可知)
具体实现
interface S{//需要修改功能的类
void d(int x,int y);
}
class R implements S{
//adaptor
void d(int x, int y){//外表
new L().b(x+y,x-y,x*y);//内部(委托)
}
}
/*
class R implements S entends C{
//adaptor
void d(int x, int y){
...
super.Func(param1,param2);
}
}
*/
class L{//可以调用或者继承的类
void b(int a,int b,int c){...}
}
class Client{
S s = new R();
public d(){
s.d(x,y);
}
}
这里需要用类L的b方法来实现类R中的d方法,从而使得Client可以正常运行,那么这里用adaptor是一个可行的方法。(疑惑:这里需要S必须是一个接口吗,为什么?我认为当S是一个具体类时,也可以通过定义一个S的子类T,然后在T中实现转接亦可,但只能用委托,而不能使用继承了。)
总结
所谓转接,外符于外,内适于内,内外不一,实现第一。
2 装饰器模式(Decorator)
该模式即lab3知道手册中的方法6
适用场景
需要用许多子类的任意组合来实现某一特殊类
思想
装饰,可以看做用多彩涂料来铺色。当然,首先我们需要一张白纸,然后根据不同颜色,一层一层的往纸上铺,可以有这种思想,我在铺红色之前,要先铺绿色;又我在铺绿色之前,要先铺灰色。那么,我们可以从灰色开始上色,然后绿色,再红色。
装饰,亦可以看做是给冰淇淋上球。首先我们需要一个筒,然后往上面上爱吃的口味。
这里的白纸和筒可以是一个实现基础功能的一个基础类,而上不同色和上不同口味球是一系列要被调用的特殊类
具体实现
这里用冰淇淋上球举例
public interface IceCream{
void AddTopping();
}
//基础类
public class PlainIceCream implements IceCream{
@override
public void AddTopping(){
cout<<"Plain";
}
}
//装饰器基类(抽象类,那么多球也总有一些相似之处吧?因此,定义一个基类更好)
public abstract class ToppingDecorator implements IceCream{
protected final IceCream **input**;//aggregation形式的委托
public ToppingDecorator(IceCream i){
this.input = i;
}
public abstract void AddTopping();//抽象方法,留给具体的装饰器类来实现
}
public class FlowerTopping extends ToppingDecorator{
public FlowerTopping(IceCream i){
super(i);//实现父类的一个好处,可以将共性的实现在父类实现,一劳永逸
}
@override
public void AddTopping(){
input.AddTopping();
cout<<"Flower";
}
}
public class IphoneTopping extends ToppingDecorator{
public iphoneTopping(IceCream i){
super(i);//实现父类的一个好处,可以将共性的实现在父类实现,一劳永逸
}
@override
public void AddTopping(){
input.AddTopping();
cout<<"iphone";
}
}
public class MoneyTopping extends ToppingDecorator{
public MoneyTopping(IceCream i){
super(i);//实现父类的一个好处,可以将共性的实现在父类实现,一劳永逸
}
@override
public void AddTopping(){
input.AddTopping();
cout<<"Money";
}
}
//Client
public Client{
public static void main(String[] args){
IceCream a = new PlainIceCream();
IceCream b = new IphoneIceCream(a);//在筒上放一个iphone
IceCream c = new CandyIceCream(b);//在放一颗糖果
IceCream d = new MoneyIceCream(c);//再放一点money
//黑暗料理。。。
d.AddTopping();//recursive实现
//**another realization**
IceCream toppingIceCream = new MoneyTopping(new IphoneTopping(new CandyTopping(new PlainIceCream())));
toppingIceCream.AddTopping();//recursive too
第一种实现可以在加money之前加点别的,或者干脆不加money,更灵活;而第二种实现不行,不喜欢money也不行~,更直接。
总结
一个白板,多种色彩,妞妞妞妞,递归实现(recursive)。
3 外观模式(Façade)
适用场合
客户端需要通过一个简化的接口来访问复杂系统内的功能
便
思想
Wrap a complicated subsystem with a simpler interface.//封装
This reduces the learning curve.//便于学习使用
It also decoupling the subsystem from its potentially many clients//解耦
Façade pattern is applied for similar kind of interfaces, its purpose is to provide a single interface rather than multiple interfaces that does the similar kind of jobs.
提供一个统一的接口来取来一系列小接口调用,相当于对复杂系统做了一个封装,简化客户端使用,便于客户端学习,解耦。
具体实现
public class Loader(){...}
public class Chess{
public static Loader Chessloader(){...}
public void ChessVA(String[] names,Loader ldr){...}
public void ChessVB(String[] names,Loader ldr){...}
public void ChessVC(String[] names,Loader ldr){...}
}
public class Go{
public static Loader Goloader(){...}
public void GoVA(String[] names,Loader ldr){...}
public void GoVB(String[] names,Loader ldr){...}
public void GoVC(String[] names,Loader ldr){...}
}
public class Monopole{
public static Loader Monoloader(){...}
public void MonoVA(String[] names,Loader ldr){...}
public void MonoVB(String[] names,Loader ldr){...}
public void MonoVC(String[] names,Loader ldr){...}
}
public HelperFacade{
public static void goandplay(Types type, Versions version, String[] names){
Loader ldr = null;
switch(type){
case CHESS:
ldr = chess.Chessloader();//静态工厂方法
Chess chess = new Chess();
switch(version){
case A:
chess.ChessVA(names);
break;
case B:
chess.ChessVB(names);
break;
...
}
break;
case GO:
Go go = new Go();
ldr = go.Goloader();
switch(version){
...
}
break;
case MONOPOLE:
...
break;
public static enum Types {CHESS,GO,MONOPOLE;}//采用枚举
public static enum Versions {A,B,C;}
}
如上设计,下面来看看使用Facade的效果。
**不使用Facade模式**:
String names = {"Giao","Skr",...};
Loader ldr = Chess.Chessloader();
Chess chess = new Chess();
chess.ChessVA(names, ldr);
**使用Facade模式**:
String names = {"Giaoi","Skr",...};
HelperFacade.goandplay(HelperFacade.Types.CHESS,HelperFacade.Versions.A,names);
//Go your game and have fun!
总结
可能看到使用Facade时,客户端的代码也并不是十分简洁,但将具体数据放在相应位置,你会发现,整个操作其实一句话就搞定了,这就是外观模式的魅力(i了i了).
杂乱无章的电线没错,但干净整洁的插座更受人喜欢。——鲁 迅(doge)
结尾
相信到了这里,你应该体会到了如何从程序的结构上解决模块之间的耦合问题,在另一模式(行为模式)中,你会看到从一个角度如何考虑解耦。