该模式可以将一个类的程序设计接口转换成另外一个接口,或者说封装一个类或对象。
适配器接受一种类型,并为其它类型产生一个接口。当你手头有一个类,而你需要的是另一个类时,可以通过该模式解决问题。
例:如果你想要使用DataGrid类少数几个成员,如第一行存储String类对象、第二行存储Int32类对象,你可以为这种需要提供一个专门的类:DataGridApater。类DataGridApater就是一个 适配器使用模式。
Adapter(编写一个具有所需要的接口的类,让后让他和拥有不同接口的类进行通信)有一下两种方法实现:通过继承和通过对象组合。第一种方式里,从一个不一致的类里派生出一个新类,然后添加需要的方法,使新的派生类能匹配所需要的接口。另一种方式是,把原始类包含在新类里创建方法转换调用。这两种方法分别叫做类适配器和对象适配器。
//
对象适配器
public
class DataGridApater
{
DataGrid myDataGrid;
//
提供对DataGrid对象的部分功能的访问
//
类DataGrid是你当前拥有的,而DataGridApater类则是你需要的。
}
当已存在的类不足提供你需要的所有功能时,可以使用类适配器,在类适配器种你可以更改某些被匹配的类的方法并使用其它未修改的方法。例:
public class OwnedClass { }
public interface IAdapter { void ChangedUnderOwnedClass(); }
public class Adapter :OwnedClass, IAdapter
{
public void ChangedUnderOwnedClass()
{ //
通过调用基类方法实现的新功能,而非纯粹的新方法。
//
如果是纯粹新方法,则仅仅是继承的实现而非Adapter模式的应用
}
}
类适配器和对象适配器之间存在一些差别,这主要表现在对象适配器的方法完全是通过更改匹配类的方法而实现的,而类适配器则不仅可以更改某些佩匹配的类的方法,还可以使用其它未修改的基类方法。
特性:1、可以通过向对象适配器的构造函数传递允许匹配的所有子类;2、对象适配器要求我们将希望使用的,被匹配对象的方法提到表面上来。
可插入的适配器:这种类型的适配器能动态地匹配几个类的适配器。通常我们根据不同的构造函数或参数设定方法决定匹配哪一个类。
Bridge模式有点像Adapter模式,都是用一个类将一种接口转换成另一种接口。但Adapter模式的意图是:使一个或多个类的接口看起来像一个特定类的接口。Bridge模式则将类的接口和它的实现分离,无需修改客户端代码就可以改变或替换实现过程。
桥接模式的参与者有:
Abstraction:定义了客户需要的服务的接口;
Refined Abstraction:客户真正需要的服务;
Implementor,它定义实现类的接口;
ConcreteImplementor,它是实现类。
桥接模式可以保持客户端程序的接口不变,而允许读者修改现实类或要使用的类。这可以防止重新编一一系列复杂的用户接口模块,而只需要重新编译Bridge和实际的最终显示类。还可以分别扩展实现类和Bridge类,二者之间通常不会有相互作用。另外客户端程序很容易隐藏实现细节。
上Bridge框架图的实现:
public class Abstraction
{
IImplementation iImp;
public Abstraction(IImplementation imp) { iImp = imp; }
public void Service1() { iImp.facility1(); iImp.facility2(); }
public void Service2() { iImp.facility3(); iImp.facility4(); }
public void Service3() { iImp.facility4(); iImp.facility1(); }
public IImplementation getImplementation() { return iImp; }
}
public class ClientService1
{
Abstraction a;
public ClientService1(IImplementation i) { a = new Abstraction(i); }
public void SeviceA() { a.Service1(); a.Service2(); }
public
void ServiceB() { a.Service3(); }
}
public class ClientService2
{
Abstraction a;
public ClientService2(IImplementation i) { a = new Abstraction(i); }
public void SeviceA() { a.Service2(); a.Service3(); }
public
void ServiceB() { a.Service1(); }
}
//
服务接口
public interface IImplementation
{
void facility1(); void facility2();
void facility3(); void facility4();
}
//
服务的具体实现
public class Implementation1 : IImplementation
{
public void facility1() { //DoSomething(); }
public void facility2() { //Dowork You need }
public void facility3() { //Do something else }
public void facility4() { //Do something still}
}
下面是客户端代码:
ClientService1 cs1 = new ClientService1(new Implementation1());
cs1.ServiceA(); cs1.ServiceB();
这里ClientService1其实可以继承自Abstraction类。如果将Abstraction类中的方法同Implementation接口的中的方法保持一一对应的关系,则对于该接口提供的任何服务都可以建立相对应的ClientService类。我们也可以将Abstraction类的Service1等方法修改为protected,而通过在派生类中添加新方法而提供服务。
public class Abstraction
{
IImplementation iImp;
public Abstraction(IImplementation imp) { iImp = imp; }
protected void Service1() { iImp.facility1(); iImp.facility2(); }
protected void Service2() { iImp.facility3(); iImp.facility4(); }
protected void Service3() { iImp.facility4(); iImp.facility1(); }
public IImplementation getImplementation() { return iImp; }
}
public class ClientService1 :Abstraction
{ public void ServiceA() { this.Service1(); }}
我们可以称上面框架图中Abstraction类为Bridger。而其派生类或Adapter类则是该Bridger类的扩展。
在看一个例子:
//
绘制一个股票的收益曲线时,通常要显示价格和一段时间的收益率,
//
而在绘制公共基金曲线时,通常要显示价格和每季度的收益。
//
如何使用Bridge来完成两种显示。
public interface Bridge
{ void ShowService();//
提供的服务
}
public class FirstGraphicBridge : Bridge
{
StocketData data;
public FirstGraphicBridgee(StocketData d) { data = d; }
public void ShowService() {//
根据GraphicShow提供服务的具体实现
}
}
public
class SecondGraphicBridge : Bridge
{
StocketData data;
public SecondGraphicBridg (StocketData d) { data = d; }
public void ShowService() {//
根据GraphicShow提供服务的具体实现
}
}
public class StocketData{ //
股票数据类
}
为提供不同服务定义不同的客户类:
//
客户更具传入的参数,而显示不同的图表
public
void ClientSevice(Bridge b) { b.ShowService(); }
//
客户代码
StocketData
data = new StocketData();
FirstGraphicBridge
b = new FirstGraphicBridge(data);
this
.ClientService(b); //
为每种显示提供一个派生自Bridge类,
//
我们可以通过接口调用而不用关心其具体实现
这里我们实际需要的服务就是显示不同的曲线,而这种实现我们使通过Bridge接口的实现类实现的。在客户端我们通过接口调用具体实现。
Bridge
模式可以作为我们书写代码的一种既定结构。我们可能会根据特定的情况在编译或运行时选择使用不同的对象,Bridge模式的目的就是为了结构化我们的代码,从而使我们可以很容易的添加新类型的前端(front-end)对象(这些前端对象是通过调用新类型的后端back-end对象的功能实现的。这么一来,就可以对前端和后端互不干扰地进行修改。)
前端类之间可以用有完全不同的接口,它们的共同指出时可以通过使用任意数量的厚端对象来实现自身的某些功能。后端对象的接口通常也不一样。后端对象之间唯一必须相同的一点是他们都实现某种类似的功能——例如,彝族采用不同方法实现的图形库,或者一些列不同的数据存取解决方案。
组合模式:所有属于部分-整体的这些元素都时可以被操作的,也就是说对某个接点/组合的操作也同样会作用于该节点的所有子接点。
通常在程序员开发的系统中,组合既可以是单个的对象,也可以是对象的集合。可以用组合模式构建部分-整体层次结构或数据的树型表示。总而言之,组合就是对象的集合,其中的每个对象既可以是一个组合,也可以是简单的对象。在树的术语中,对象可以是带有其它分支的节点,也可以是叶子。
开发方面存在的问题是对组合中的所有对象都要求具有一个简单的、单一访问接口并要求能够区分节点与叶子,这二者时相互矛盾的。,节点可以有孩子并允许添加孩子,而叶子节点不允许有孩子,在某些实现中要防止对他们添加孩子节点。
有人建议,为了节点和叶子传建一个单独的接口,如:
//
叶子具有的方法
public string getName() { }
public float getValue() { }
//
节点具有的方法
public ArrayList ContainElements() { }
public void add(object obj) { }
public void remove(object obj) { }
public Node getChild(String nodeName) { }
问部分-整体结构和树形结构有什么区别?从数据结构的角度考虑,两者都是可以用树来表示的。
装饰模式:改变单个对象的行为,但不需要创建一个新的派生类。
Decorator模式是指:用分层的对象(layered objects)动态地和透明地给单个对象添加功能(responsibilities)。 当使用派生类方法产生过多(不灵活的)类的时候考虑应用Decorator模式。 所有外覆在原始对象上的装饰类都必须有同样的基本接口。
Decorator
模式对于非常规的继承结构特别管用。
装饰模式提供了一种给一个类添加职责的方式,它比使用继承更加灵活,因为它能将职责添加到指定实例中,它也允许我们定制一个类,而无需再继承层次结构中创建高层子类。“Design Patterns”中指出该模式的两个缺点,一个是Decorator和它包含的组件是不一样的,这样,检测对象类型时会失败;另一个是装饰模式会是一个系统带有“大量的小对象”,对于维护代码的程序员来说,他们看起来都差不多,维护起来会很麻烦。
在生成构架中,装饰模式和外观模式会产生相似的图像,但在设计模式的术语中,外观模式是将复杂系统隐藏在一个简单接口里,而装饰模式通过包装一个类来增加功能。
例:关于
Decorator
模式,《设计模式》一书是这么说的
:
“
通过附加(
attching
)或者分离(
detaching
)装饰者(
decorators
),可以在运行时刻(
run-time
)添加或者去处某项功能(
responsibilities
)
”
。写一个咖啡的
Decoration
系统,这个系统可以从某种复杂的咖啡饮料的一系列添加物(
decorators
)里
“
简单
”
的去除某项功能。
Decorator
类首先就具备其所装饰的组件所具有的功能。一窗户不论怎么装饰,它还是窗户,不可能变成桌子。改变的仅仅是我们想其中添加了窗帘、或其它什么东西。由装饰词义我们就可以想到该则装饰一个类。
对于这种模式的使用我还存在疑问。
外观模式对客户屏蔽了复杂的子系统组建,并为一般用户提供一个比较简单的程序设计接口。但是,它并没有限制高级用户在需要时使用深层的、较复杂的类。另外还允许我们改变底层的子系统而不需要修改客户代码,降低了编译依赖性。
当我们试图将需要初步转化成对象的时候,如果存在复杂的类以及交互,而客户端程序员又不需要了解这些复杂的事情,我们可以为客户端程序员创建一个接口只提供那些必要的功能。这就是外观模式的。
Façade模式经常被实现为一个符合Singleton模式的Abstract factory。其实我觉得.NET Framework框架就是一个外Façade模式样例。
该模式可以避免大量非常相似的类的开销。在程序中会出现这样的情况,看起来似乎需要生成大量细粒度的类实例来表示数据。如果能发现这些实例除了几个参数外基本上都是相同的,有时就能够大幅度地减少需要实例化的类的数量。如果能把那些参数移到实例外面去,在方法调用时将他们传递进来,就可以通过共享大幅度地减少单个实例的数目。
Flyweight模式涉及到使实例唯一的内部数据和作为参数的外部数据。用少量的实例表示程序中的大量对象,这就存在数据共享问题。当程序代码要修改实例唯一的内部数据时,就应当考虑是拒绝修改还是创建新的对象实例。存在共享的内部数据的修改这一过程通常被称为Copy-on-Write,Flyweight和许多其它的类都用到这个过程。
当我们需要将一个复杂的对象或创建时比较花费时间的对象表示成一个简单对象时,可以使用代理模式。如果创建一个对象比较浪费时间或计算机资源,Proxy允许将创建过程推迟到需要该实际对象的时候。Proxy对象通常具有和它所代表的对象一样的方法,一旦对象被调用,就把调用方法从Proxy传递给实际对象。
以下几种情况适合于时用Proxy:
1、调用一个对象需要花费很长的时间,如调入一个大图像。
2、计算结构需要花费很长时间才能完成,计算过程中需要显示中间结果。
3、对象位于远程机器上,通过网络调入会很慢,特别是网络负载高峰期间。
4、对象限制了访问权限,代理可以使访问许可对用户有效。
Proxy还可以区别是请求一个对象实例,还是真正需要访问该实例。如,程序初始化时建立了许多对象,但不是所有的对象马上就被调用,这种情况下,代理能在需要时调入真正的对象。
又如一个程序需要调入一个大图像并显示出来。程序运行开始时,必须对显示图象有些说明,这样才能正确布局屏幕,但真正的图像显示可以推迟到图像完全调入后。
public class ImageProxy
{
Image img;
public Image Img { get { return img; } }
public ImageProxy() { img = new Bitmap("Box.gif"); }
public Image GetImage()
{ img = new Bitmap("FlowTree.jpg"); }
}
这个图像代理在构造时将img成员设置为Box.gif图像而非我们真正需要的图。这就如同浏览网站时图像显示,如果存在网络延迟时,我们总是看到一个方框而后才看到真正的图像。当然这个例子没有实际意义,在实际使用中,代理的构造函数应当处理图像布局的一些问题,而当真正图像下在完后(调入成功后),代理应意识到并将真正图像显示。
代理其实就是作为一个中转站,我们一般可以有:
//
服务的提供者
public class Service
{
public object SupportService { }
//ther member
}
//
客户端代码使用Proxy类调用Service类的方法
public class Proxy
{
//From here you could get the service
private Service ser;
public Proxy(){ ser = new Service();}
public object GetService()
{
ser.SupportService();
}
}
C#中Context-bounds对象就是利用代理来实现不同Context的交互的,然而.NET Framework封装了代理的实现。问:这种封装是如何实现的?如何用下面Service将Proxy封装起来。
//
服务的提供者
public class Service
{
public object SupportService { return null;}
//ther member
}
//
客户端代码使用Proxy类调用Service类的方法
public class Proxy
{
//From here you could get the service
private Service ser;
public Proxy(){ ser = new Service();}
public obje GetService()
{
ser.SupportService(); return null;
}
}
思考:设计一个连接到数据库的服务器,如果有几个客户终端要同时连接到服务器上,如何使用Proxy?