Section 7 适配器模式和外观模式 Adapter & Facade
>客户使用适配器 Client - Request -> Adapter -> TranslateRequest - Adaptee
客户通过目标接口调用适配器的方法发出请求; 适配器使用被适配者的接口把请求转换成被适配者的一个或多个调用接口; 客户收到调用结果, 并不知道是适配器在转化请求
>双向适配器, 实现2个接口, 新旧并存
适配器模式 将一个类的接口转化成客户期望的另一个接口. 让原本接口不兼容的类一起工作.
>让接口兼容, 实现接口的去耦合
对象和类的适配器
>类适配器需要多重继承, Java无法完成 >对象适配器-组合; 类适配器-继承;
>适配器文档, 使用说明
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
|
//***********Adapter & Facade**********
class IPanda
{
public :
virtual void eat() = 0;
virtual void climb() = 0;
};
class IBear
{
public :
virtual void eat() = 0;
virtual void creep() = 0;
};
class BigPanda : public IPanda
{
public :
virtual void eat() { cout << "Eat Bamboo" << endl ;}
virtual void climb() { cout << "Climb" << endl ;}
};
class BlackBear : public IBear
{
public :
virtual void eat() { cout << "Eat Fish" << endl ;}
virtual void creep() { cout << "Creep" << endl ;}
};
class BearAdapter : public IPanda
{
public :
BearAdapter(IBear* bear);
virtual void eat() { mpBear->eat(); }
virtual void climb() { mpBear->creep(); }
private :
IBear* mpBear;
};
//class BigPanda : virtual public IPanda
class BearAdapterClass : public BigPanda, public BlackBear
{
public :
virtual void climb() { BlackBear::creep(); }
};
|
1
2
3
4
|
BearAdapter::BearAdapter(IBear* bear)
: mpBear(bear)
{
}
|
1
2
3
4
5
6
7
8
|
//Adapter & Facade
BigPanda* panda = new BigPanda();
BlackBear* bear = new BlackBear();
BearAdapter* bearAdapter = new BearAdapter(bear);
bearAdapter->eat();
bearAdapter->climb();
BearAdapterClass* bearAdapterClass = new BearAdapterClass();
bearAdapterClass->climb();
|
外观模式 提供了一个统一的接口, 访问子系统中的一群接口. 外观定义了高层接口, 让子系统更加容易使用.
简化接口, 将客户从组件的子系统中解耦; 外观和适配器可以包装许多类, 外观是为了简化接口, 适配器是为了转换接口
设计原则 最少知识原则: Least Knowledge 只和你的密友谈话 (Law of Demeter)
>不要让太多的类耦合在一起, 互相依赖会难以维护, 增加复杂性; 对任何对象而言, 在该对象的方法内, 只应该调用属于以下范围的方法: 对象本身; 被当作方法的参数传递进来的对象;
此方法所创建或实例化的对象; 对象的组件(有一个 HAS A);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
class Amplifter
{
public :
void On() { cout << "Amplifter turn on" << endl; }
void SetVolume( int v) { cout << "Amplifter volume" << v << endl; }
void Off() {cout << "Amplifter turn off" << endl;}
};
class DVDPlayer
{
public :
void On() { cout << "DVDPlayer turn on" << endl; }
void Play(string movie) {cout << "Play movie <" << movie << ">" << endl;}
void Off() { cout << "DVDPlayer turn off" << endl; }
};
class HomeTheaterFacade
{
public :
HomeTheaterFacade(Amplifter* amp, DVDPlayer* dvd);
void watchMovie(string movie);
void endMovie();
private :
Amplifter* mpAmp;
DVDPlayer* mpDVD;
};
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
HomeTheaterFacade::HomeTheaterFacade(Amplifter* amp, DVDPlayer* dvd)
:mpAmp(amp),
mpDVD(dvd)
{
}
void HomeTheaterFacade::watchMovie(string movie)
{
mpAmp->On();
mpAmp->SetVolume(7);
mpDVD->On();
mpDVD->Play(movie); //...
}
void HomeTheaterFacade::endMovie()
{
mpAmp->Off();
mpDVD->Off();
}
|
1
2
3
|
HomeTheaterFacade* homeTheater = new HomeTheaterFacade( new Amplifter(), new DVDPlayer());
homeTheater->watchMovie( "The.Dictator" );
homeTheater->endMovie();
|
Summary
>OO原则 只和朋友交谈 >OO模式 适配器模式, 外观模式
>要点 当需要一个现有的类但是接口不符合需求时使用适配器模式; 当需要简化一个庞大的接口或者一群复杂的接口时使用外观模式; 适配器模式改变接口以符合客户期望; 外观模式
将客户从复杂系统中解耦; 实现适配器的工作量由目标接口的大小和复杂度来定; 实现外观需要将子系统组合进外观中, 工作委托给子系统执行; 对象适配器和类适配器(多重继承);
可以为一个子系统实现多个外观;
>适配器将对象包装起来,改变接口转化成需要的接口; 装饰者将对象包装起来, 不改变接口, 增加新的行为和责任; 外观将一群对象包装起来, 简化接口
SimplePassThrough, Decoupling, LeastKnowledge, Converts, Decorator, Acadapter, TwoWay, Target, Wrappers, Facade, Wrap
---Section 7 End---
Section 8 模板方法模式 Template Method
封装算法, 定义了一个算法的步骤, 允许子类为一个或者多个步骤提供实现.
模板方法模式 在一个方法中定义算法的骨架, 将一些步骤延迟到子类中. 使得子类可以在不改变算法结构的情况下, 重新定义算法中的某些步骤.
>将算法定义成一组步骤的方法, 任何步骤都可以是抽象的, 由子类负责实现. 算法的结构不变, 由子类提供部分实现.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
class CaffeineBeverage
{
public :
void prepareRecipe();
virtual void brew() = 0;
virtual void addCondiments() = 0;
void pourInCup() { cout << "Pouring in cup" << endl; }
virtual bool customerNeedCondiments() { return true ; }
};
class Coffee : public CaffeineBeverage
{
public :
Coffee() : mbNeed( true ) {};
virtual void brew() { cout << "Dripping coffee" << endl; }
virtual void addCondiments() { cout << "Adding sugar and milk" << endl; }
virtual bool customerNeedCondiments() { return mbNeed;}
void setNeed( bool bNeed) {mbNeed = bNeed;}
private :
bool mbNeed;
};
class Tea : public CaffeineBeverage
{
public :
Tea() : mbNeed( true ) {};
virtual void brew() { cout << "Steeping tea" << endl; }
virtual void addCondiments() { cout << "Adding lemon" << endl; }
virtual bool customerNeedCondiments() { return mbNeed;}
void setNeed( bool bNeed) {mbNeed = bNeed;}
private :
bool mbNeed;
};
|
1
2
3
4
5
6
7
8
|
//***********Template**********
void CaffeineBeverage::prepareRecipe()
{
brew();
pourInCup();
if (customerNeedCondiments())
addCondiments();
}
|
1
2
3
4
5
6
|
//Template
Tea* tea = new Tea();
Coffee* coffee = new Coffee();
coffee->setNeed( false );
tea->prepareRecipe();
coffee->prepareRecipe();
|
好莱坞原则 别调用(call)我们, 我们会调用(call)你
>防止依赖腐败, 高层组件依赖低层组件, 低层依赖高层, 高层依赖边侧组件, 边侧依赖低层时产生的依赖腐败; 高层控制调用低层组件, 低层不可以直接调用高层.
>JAVA Applet类大量使用Hook来提供行为.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
public abstract class AbstractClass {
final void tempMethod(){
primitiveOperation1();
primitiveOperation2();
concreteOperation();
hook();
}
abstract void primitiveOperation1();
abstract void primitiveOperation2();
final void concreteOperation() {
// Implement
}
void hook() {}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
|
public class MyFrame extends JFrame {
public MyFrame(String title){
super (title);
this .setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this .setSize( 200 , 300 );
}
public void paint(Graphics graphics){
super .paint(graphics);
String msg = "Hello hook" ;
graphics.drawString(msg, 100 , 150 );
}
}
|
1
2
3
|
//Template Hook
MyFrame myFrame = new MyFrame( "Head First" );
myFrame.setVisible( true );
|
>依赖倒置原则 尽量避免使用具体类, 多使用抽象; 好莱坞原则 创建框架或组件的技巧, 让低层组件挂钩到计算中, 不会让高层组件依赖低层组件; 目标都是解耦.
依赖倒置原则注重在设计中避免依赖; 好莱坞原则技巧: 创建有弹性的设计, 允许低层结构互相操作, 防止其他类依赖底层. 避免环状依赖.
>策略模式 组合的类实现了整个算法, 封装可互换的行为, 使用委托来决定采用哪一个行为; 模板方法 实现部分算法, 子类决定如何实现算法中的某些步骤; 工厂方法 具体类.
Summary
>OO原则 别找我, 我会找你
>要点 定义算法步骤, 延迟到子类实现; 代码复用; 抽象类可以定义具体方法, 抽象方法和钩子; 抽象方法由子类实现; 钩子方法在抽象类中不做事或者有默认行为, 子类可以选择是否覆盖它;
防止子类改变模板方法中的算法, JAVA用final; 好莱坞原则 将决策权放在高层模块中, 决定如何及何时调用低层模块; 真实代码中的模板方法有许多变体; 策略模式和模板方法都封装算法,
一个用组合, 一个用继承; 工厂方法是模板方法的特殊版本;
Composition, Optional, Specialization, Inheritance, Abstract, Strategy, Algorithm, Static, Applet
---Section 8 End---