结构型模式-适配器模式

情景:接到一个新任务,需求是实现一个简单的绘图编辑器,工具需要基本满足绘制常用图元(线,多边形,正文等)以生成图片和图表。

分析:绘图编辑器的关键抽象是图形对象,图形对象需要满足能够绘制自身,且拥有一个可编辑的形状(绘制时的状态保存)。常见的,图形对象的接口可以由一个称为Shape的抽象类定义,那么接下来我们便可以对每一种具象的图像对象定义成Shape的子类,比如直线可以是LineShape,多边形可以是PolygonShape等。

问题:现在看来一切都在正常进行的,LineShape跟PolygonShape的工作得很好,基本满足了我们得需求,但当我们开始着手“正文”图形对象时,问题出现了,因为即使是基本的正文编辑也要涉及到复杂的屏幕刷新和缓冲区管理,同时,成品的用户工具箱可能已经提供了一个复杂的TextView类用于显示和编辑正文,一种可行的做法是使用TextView类兼容Shape的接口以实现TextShape类,这看起来不错,但事实上我们不应该仅仅为了实现一个应用(绘图编辑器),就让成品工具箱内的TextView去采用一些与特定领域相关的接口。

解决方案:使用TextShape来适配TextView跟Shape接口(在不改变TextView的类结构的情况下),我们可以有两种方法做这件事:

1.TextShape继承Shape接口跟TextView的实现,

2.将整个TextView实例作为TextShape的组成部分,并且使用TextView接口实现TextShape。

这两种方法其实就是Adapter模式的类和对象版本,我们将TextShape称之为适配器Adapter。

适配器模式-类图png

上图主要说明了在Shape类中声明的BoundingBox请求(获取边框两点坐标)是如何被转换成TextView类中定义的GetExtent请求(获取Text长和宽)+GetOrigin请求(获取Text左下坐标-->原点),这样TextShape将TextView接口与Shape接口进行了匹配,绘图编辑器就可以复用原先不兼容的TextView类。此外,Shape对象提供CreateManipulator请求,Manipulator是一个抽象类,其知道如何驱动Shape类响应用户的各类操作,例如将图形拖动到一个新的位置。Manipulator有不同的子类,例如子类TextManipulator对应TextShape。

关键字:
Target--定义Client使用的与特定领域相关的接口(Shape)

Client--与符合Target接口的对象协同(绘图编辑器)

Adaptee--定义一个已经存在的接口,这个接口需要适配(TextView)

Adpater--对Adaptee接口与Target接口进行适配(TextShape)

实现:

struct Point
{
public:
    Point(int x, int y) :m_iX(x), m_iY(y) {};

    int m_iX;
    int m_iY;
};

class Manipulator
{
public:
    Manipulator(Shape *pShape) :m_pShape(pShape) {};
    virtual ~Manipulator();

    virtual bool MoveToPos(const Point &pointStart, const Point &pointEnd) = 0;

private:
    Shape *m_pShape;
};

class Shape
{
public:
    Shape();
    virtual void BoundingBox(Point &bottomLeft, Point &topRight) const;
    virtual Manipulator *CreateManipulator();
};

class TextView
{
public:
    TextView();
    void GetOrigin(int &x, int &y) const;
    void GetExtent(int &width, int &height) const;
    virtual bool IsEmpty() const;
};

class TextManipulator :public Manipulator
{
public:
    TextManipulator(Shape *pShape);
    virtual ~TextManipulator();

    virtual bool MoveToPos(const Point &pointStart, const Point &pointEnd);
};

类适配器模式(多继承适配)

class TextShape : public Shape, private TextView
{
public:
    TextShape();
    virtual void BoundingBox(Point &bottomLeft, Point &topRight) const;
    virtual bool IsEmpty() const;
    virtual Manipulator *CreateManipulator();
};

void TextShape::BoundingBox(Point &bottomLeft, Point &topRight) const
{
    int x, y, width, height = 0;

    GetOrigin(x, y);
    GetExtent(width, height);

    bottomLeft = Point(x, y);
    topRight = Point(x + width, y + height);
}

bool TextShape::IsEmpty() const
{
    return TextView::IsEmpty();
}

Manipulator *TextShape::CreateManipulator()
{
    return new TextManipulator(this);
}

对象适配器模式(对象组合)

class TextShape : public Shape
{
public:
    TextShape(TextView *textView);
    virtual void BoundingBox(Point &bottomLeft, Point &topRight) const;
    virtual bool IsEmpty() const;
    virtual Manipulator *CreateManipulator();

private:
    TextView *m_pTextView;
};

TextShape::TextShape(TextView *textView)
{
    m_pTextView = textView;
}

void TextShape::BoundingBox(Point &bottomLeft, Point &topRight) const
{
    int x, y, width, height = 0;

    m_pTextView->GetOrigin(x, y);
    m_pTextView->GetExtent(width, height);

    bottomLeft = Point(x, y);
    topRight = Point(x + width, y + height);
}

bool TextShape::IsEmpty() const
{
    return m_pTextView->IsEmpty();
}

Manipulator *TextShape::CreateManipulator()
{
    return new TextManipulator(this);
}

适用性:

  • 你想使用一个已经存在的类,而它的接口不符合你的要求(TextView)
  • 你想使用一些已经存在的子类,但是不可能对每一个都进行子类化以匹配它们的接口,对象适配器模式可以适配它们的父类接口

选择:
选择类适配器与对象适配器有不同的权衡。

类适配器

  • 使得Adapter可以重定义Adaptee的部分行为,因为Adapter是Adaptee的一个子类(多继承)。
  • 不需要额外的指针以间接得到Adaptee。

对象适配器

  • 允许一个Adapter与多个Adaptee--即Adaptee本身以及它的所有子类(如果有的话)一同工作。也可以一次给所有的Adaptee添加功能。

应用:
1.在C++ STL中广泛使用了Adapter模式,主要有container adapter、iterator adapter、functor adapter:

*container adapter: stack, queue(数据结构)

*iterator adapter: front_insert_iterator, back_insert_iterator, istream_iteator, ostream_iterator

*functor adapter: bind, negate, compose (与对一般函数或者成员函数的修饰)

2.Android ListView中Adapter模式
等等

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,222评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,455评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,720评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,568评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,696评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,879评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,028评论 3 409
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,773评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,220评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,550评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,697评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,360评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,002评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,782评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,010评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,433评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,587评论 2 350