设计模式(4)-适配器(Adapter)模式

来源:互联网 发布:大数据概念龙头股 编辑:程序博客网 时间:2024/06/05 07:14

理解适配器就明白什么是适配器模式,适配器模式本就来源于生活中适配器的启发。

那么什么是适配器呢?。

场景

适配器的作用就是使用户能够通过统一的接口,复用已有的但接口不统一的功能。好吧,我承认这一句话比较抽象。

有一个古老的问题,一只动物,看上去是羊,听上去是羊,那他就是羊。是真的吗?

有一只狼,他想混进羊群,但是有放牧人看着他发现很难直接混进去。他找来了一张羊皮,披上这张羊皮之后,他成功的骗过了牧羊人的眼睛。

这个牧民就是客户,这个羊就是目标类,这个狼就是被适配类,这个羊皮就适配器。羊表现得像个羊,比如吃草,那牧民就认为这是羊。

羊皮的作用就是使用户(牧民)通过统一的(吃草)接口,复用已有的(狼)但接口不统一(吃肉)的功能

结构

适配器有两种结构
一种是通过多重继承实现适配器类(类适配器)
这里写图片描述
一种是通过单继承但维护一个被适配对象实现适配器类(对象适配器)
这里写图片描述

注意上面两种适配器类的区别,一个通过多重继承实现,一个通过维护一个被适配对象实现

实现

我个人还是喜欢对象适配器的模式(不喜欢类适配器的多重继承是其一,同是对象适配器具有更高的灵活性),所以实现就实现这一个吧。

这两种具体原理没有本质区别,都是调用这个统一接口的时候,在函数内部将调用转接到被适配的接口就行了。不同的是形式,不做过多讲。

class Herder//牧民{public:    void graze(Sheep* sheep) {sheep->eatGrass();};//放牧(羊),让羊吃草};class Sheep//羊{public:    virtual void eatGrass();//吃草    virtual ~Sheep();};class SheepSkin:public Sheep//羊皮出在羊身上{public:    SheepSkin(Wolf* wolf) :_wolf(wolf) {};//狼披上羊皮    void eatGrass() { _wolf->eatMeat(); };//狼假装吃草,实际吃肉    virtual ~SheepSkin();private:    Wolf * _wolf=0;};class Wolf//狼{public:    void eatMeat();//狼吃肉};

上面的示例代码中,SheepSkin是适配器类,将狼和羊的吃草(eatGrass)和吃肉(eatMeat)接口做了适配,Herder是客户端,这个粗心的牧羊人在放牧的时候,只以为所有羊都在吃草,他不知道到底在吃什么,但是看上去是吃草。

这就是适配器的作用,隐藏了被适配对象,实现了目标类和被适配类的适配。

类用户不管被适配对象到底是谁,反正我只调用这一个接口,具体接下来怎么做,到底是吃草还是吃肉,完全交给这个目标类(羊)的子类(适配器)来决定。

优点

1.使用户能够通过统一接口复用已有类的功能;
2.解耦用户与被适配类,两者都无需修改,只需要增加中间类(适配器);
3.对象适配器,具有更高灵活性,被适配对象可替换(多态)。

思考

如果一个系统用到了适配器模式,这是一种好的设计吗,为什么不直接调用被适配对象呢?

1.先回答为什么不直接调用被适配对象,如果用户直接调用被适配对象,无法实现统一接口,增加了用户与这个类的耦合度,扩展性受到影响。

2.然后回答最初的问题,我个人认为这恐怕不是一种好的设计,出现这种接口不统一,本来就不是一种好的设计,狼吃肉,羊吃草,他们的共同动作本来是吃,而且他们本来就应该是动物(Animal)的子类,吃这个动作已经足够了, 为什么要分成完成独立的两个类呢,分成吃草吃肉?这本来就是一种设计的失误,作为补救,我们创造了适配器模式。

所以结论就是,适配器模式是不得已的选择。出现适配器模式,本来就是设计的失误(遗留模块,需求变更等问题也会导致)。也许这个观点是错误的,毕竟我见的想的还是太少了,但至少现在我会尽量避免使用。

原创粉丝点击