当前位置:首页 > 公众号精选 > 码农爱学习

本篇文章,来解读《大话设计模式》的第6章——装饰模式。并通过C++代码实现实例代码的功能。

1 装饰器模式

装饰模式,或称装饰器模式(Decorator),动态地给一个对象添加一些额外的职责,就增加功能来说,装饰模式比生成子类更加灵活

我们在给对象增加功能时,一种做法是再设计一个子类来继续父类,然后给子类添加额外的功能,另一种做法就是通过装饰模式,设计单独用于装饰功能的类,通过“包装”的形式动态给某个对象增加功能。

2 穿搭衣服实例

题目:用控制台程序,写可以给人搭配衣服的代码

2.1 版本一

版本一的代码,仅定义了一个Person类,提供6种不同服饰的装扮接口,以及1个名字展示接口。

2.1.1 Person类

Person类的代码如下,维护一个人的名字,然后是6种服饰的穿衣接口,就是加一句打印,最后Show接口显示人的名字。

class Person
{

public:
Person(std::string name)
{
m_name = name;
}

void WearTShirts()
{
printf("大T恤 ");
}

void WearBigTrouser()
{
printf("垮裤 ");
}

void WearSneakrs()
{
printf("破球鞋 ");
}

void WearSuit()
{
printf("西装 ");
}

void WearTie()
{
printf("领带 ");
}

void WearLeatherShoes()
{
printf("皮鞋 ");
}

void Show()
{
printf("装扮的%s", m_name.c_str());
}

private:
std::string m_name;
};

2.1.2 主函数

主函数的逻辑如下,先实例化一个名为"小菜"的Person对象,然后依次调用穿衣接口,最后调用展示接口:

#include 

int main()
{
Person xc = Person("小菜");

printf("\n第一种装扮:");
xc.WearTShirts();
xc.WearBigTrouser();
xc.WearSneakrs();
xc.Show();

printf("\n第二种装扮:");
xc.WearSuit();
xc.WearTie();
xc.WearLeatherShoes();
xc.Show();

printf("\n");

return 0;
}

代码运行效果如下:

版本一这种方式,虽然功能实现了,但如果想要增加装扮,就需要修改Person类了,这违反了面向对象设计中的开放-封闭原则。

开放-封闭原则:是指软件实体(类、模块、函数等等)应该可以扩展,但是不可修改。

换句话说:

  • 开放:对扩展开放,当需要增加新功能时,通过代码扩展的方式(如增加新的类)实现
  • 封闭:对修改封闭,当需要增加新功能时,尽量避免对原有代码的修改

下面来看版本二如何实现。

2.2 版本二

版本二是将各种服饰单独封装了起来,并继承自服饰抽象类,将服饰类与人类进行了分离,类图如下:

这样,后续需要增加服饰时,只需要增加对应的具体服饰类,而不会影响其它已有的服饰类的代码。

2.2.1 Person类与服饰类

Person类与服饰类的代码如下,Person类只维护一个人的名字,并通过Show接口显示人的名字。服饰类的Show接口用于显示服饰的名称,具体显示的内容由具体服饰类的Show接口实现,也是打印出服饰的名字。

// Person类
class Person
{

public:
Person(std::string name)
{
m_name = name;
}

void Show()
{
printf("装扮的%s", m_name.c_str());
}

private:
std::string m_name;
};

// 服饰类
class Finery
{

public:
virtual void Show(){};
};

// 各种服饰子类
class TShirts : public Finery
{
public:
void Show()
{
printf("大T恤 ");
}
};

class BigTrouser : public Finery
{
public:
void Show()
{
printf("垮裤 ");
}
};

class Sneakrs : public Finery
{
public:
void Show()
{
printf("破球鞋 ");
}
};

class Suit : public Finery
{
public:
void Show()
{
printf("西装 ");
}
};

class Tie : public Finery
{
public:
void Show()
{
printf("领带 ");
}
};

class LeatherShoes : public Finery
{
public:
void Show()
{
printf("皮鞋 ");
}
};

2.2.2 主函数

主函数的逻辑如下,先实例化一个名为"小菜"的Person对象,

然后依次实例化具体要装扮的服饰类并调用对应的展示接口,

最后调用Person的展示接口:

int main()
{
Person xc = Person("小菜"); //先实例化一个名为"小菜"的Person对象

printf("\n第一种装扮:");
TShirts dtx; //依次实例化具体要装扮的服饰类
BigTrouser kk;
Sneakrs pqx;
dtx.Show(); //调用对应的展示接口
kk.Show();
pqx.Show();
xc.Show(); //最后调用Person的展示接口

printf("\n第二种装扮:");
Suit xz;
Tie ld;
LeatherShoes px;
xz.Show();
ld.Show();
px.Show();
xc.Show();

printf("\n");

return 0;
}

代码运行效果如下:

版本二中,Rerson类和服饰类是完全独立的,搭配衣服的过程也只是一个一个将对应词打印出来。下面来看版本三。

2.3 版本三

版本三用到了本篇要讲的装饰器模式。

在本例中,服饰就是装饰类,具体的服饰,T恤、球鞋这些是具体的装饰类,而人就是要被装饰的组件,那是组件Component还是具体组件ConcreateComponet呢?

本例中,只有一个ConcreateComponet具体组件类,就没必要单独建立一个Component组件类,可以把两者职责合并成一个类,也就是只使用ConcreateComponet类表示Person类。

2.3.1 Person类与服饰类

Person类与服饰类的代码如下:

// Person类
class Person
{

public:
Person(){};

Person(std::string name)
{
m_name = name;
}

// 父类的函数
virtual void Show()
{
printf("装扮的%s", m_name.c_str());
}

private:
std::string m_name;
};

// 服饰类(Decorator)
class Finery : public Person
{
protected:
Person *m_pComponent = nullptr;

public:
void Decorate(Person *pComponent)
{
m_pComponent = pComponent;
}

// 子类的函数
void Show()
{
if (m_pComponent != nullptr)
{
m_pComponent->Show();
}
}
};

// 具体服饰类(ConcreateDecorator)
class TShirts : public Finery
{
public:
void Show()
{
printf("大T恤 ");
Finery::Show();
}
};

class BigTrouser : public Finery
{
public:
void Show()
{
printf("垮裤 ");
Finery::Show();
}
};

class Sneakrs : public Finery
{
public:
void Show()
{
printf("破球鞋 ");
Finery::Show();
}
};

class Suit : public Finery
{
public:
void Show()
{
printf("西装 ");
Finery::Show();
}
};

class Tie : public Finery
{
public:
void Show()
{
printf("领带 ");
Finery::Show();
}
};

class LeatherShoes : public Finery
{
public:
void Show()
{
printf("皮鞋 ");
Finery::Show();
}
};

2.3.2 主函数

主函数的逻辑如下,先实例化一个名为"小菜"的Person对象,

然后再依次实例化要装扮的服饰类对象,接着通过“包装”的方式,后一个对象对前一个对象进行包装,

最后调用最终包装后对象的展示接口:

int main()
{
Person *xc = new Person("小菜");

printf("\n第一种装扮:");
TShirts *dtx = new TShirts();
BigTrouser *kk = new BigTrouser();
Sneakrs *pqx = new Sneakrs();

dtx->Decorate(xc); // 装饰过程:先用大裤衩装饰小菜
kk->Decorate(dtx); // 再用垮裤装饰穿了大裤衩的小菜
pqx->Decorate(kk); // 再用破球鞋装饰穿了大裤衩和垮裤的小菜
pqx->Show(); // 最后调用最外层的装饰对象的展示接口

printf("\n第二种装扮:");
Suit *xz = new Suit();
Tie *ld = new Tie();
LeatherShoes *px = new LeatherShoes();

xz->Decorate(xc); // 装饰过程:先用西装装饰小菜
ld->Decorate(xz); // 再用领带装饰穿了西装的小菜
px->Decorate(ld); // 再皮鞋装饰穿了西装和领带的小菜
px->Show(); // 最后调用最外层的装饰对象的展示接口

printf("\n");

return 0;
}

代码运行效果如下:

装饰模式的优缺点与适用场景:

3 总结

本篇介绍了设计模式中的装饰模式,并通过给人装扮的实例,使用C++编程,来演示装饰模式的使用。

本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除( 邮箱:macysun@21ic.com )。
关闭