装饰模式
装饰模式
(Decorator)动态的给一个对象添加一些额外的职责。就增加功能来说,装饰模式比生成子类更为灵活。将类中的装饰功能从类中搬移去除,这样简化了原有的类。
引子
做一个人可以搭配不同服装的系统。
版本一:
package decorator;
public class Person1 {
private String name;
public Person1(String name)
{
this.name=name;
}
public void WearBigTrouser()
{
System.out.println(this.name+"穿垮裤");
}
public void WearSneakers()
{
System.out.println(this.name+"破球鞋");
}
public void WearSuit()
{
System.out.println(this.name+"穿西装");
}
public void WearLeatherShoes()
{
System.out.println(this.name+"穿皮鞋");
}
public static void main(String[] args) {
Person1 p1=new Person1("菜菜");
p1.WearBigTrouser();
p1.WearLeatherShoes();
}
}
如果按照这种写法,当我们需要增加一件装扮的时候,就需要去改动Person1这个类,这就违背了 开放-封闭原则,衣服这种需求是会经常变化的,对于这种可能存在变化的功能,我们一般会将它抽象出来,利用继承、多态,使其满足多变的需求。
抽象 皮肤类
package decorator;
public interface Skin {
public abstract void MyShow();
}
头盔皮肤
package decorator;
public class HeadSkin implements Skin{
@Override
public void MyShow() {
// TODO Auto-generated method stub
System.out.println("鸡冠头");
}
}
全身皮肤
package decorator;
public class ClothesSkin implements Skin {
@Override
public void MyShow() {
// TODO Auto-generated method stub
System.out.println("金光闪闪的全身");
}
}
客户端代码:
package decorator;
public class Person2 {
public String name;
public Person2(String name)
{
this.name=name;
}
public void MyClothes()
{
System.out.println("这就是我的新衣");
}
public static void main(String[] args) {
Skin s=new HeadSkin();
s.MyShow();
Skin s2=new ClothesSkin();
s2.MyShow();
Person2 p2=new Person2("菜");
p2.MyClothes();
}
}
结果:
鸡冠头
金光闪闪的全身
这就是我的新衣
这个时候,如果我们需要增加裤子或者换其他款式的衣服,我们并不需要去修改,暴露原来已经写好的其他衣服的设计的代码,只需 增加一个 Skin的子类 即可。
仔细观察上面代码,会发现一个问题,人和衣服是分开的,感觉就像是 ‘鸡冠头’,‘金光闪闪的全身’,‘这就是我的新衣’ ,这三件 一个一个拼接显示出来, 这样子显然不妥,我们期望的是 能够在 内部组装完毕,然后再显示出来
,穿衣,衣服的先后顺序不同,搭配出来的样子也是不同的,所以先后顺序也是有讲究的,我们需要把所需的功能按正确的顺序串联起来进行控制
,这就需要我们来学习一个模式--装饰模式
装饰模式
装饰模式
动态的给一个对象添加一些额外的职责,从增加功能来说,装饰模式比生成子类更加灵活。
例子: 不同的身份可能会搭配不同的服装,人的身份,服装都是会改变的。
person类,作为基类,有姓名的属性 和消费的 功能
package decorator;
public abstract class Person {
protected String name;
public void SetName(String name)
{
this.name=name;
}
public String GetName()
{
return this.name;
}
public String getDescription()
{
return this.name+"买了:";
}
public abstract double cost();
}
人的身份是不同且多变的,有医生,程序员,还有法师,还有战斗法师等等
青少年 类:
package decorator;
public class Teenager extends Person{
public Teenager(String name)
{
this.name=name;
}
@Override
public double cost() {
// TODO Auto-generated method stub
return 0;
}
}
年轻少女 类
package decorator;
public class YoungLady extends Person{
public YoungLady(String name)
{
this.name=name;
}
@Override
public double cost() {
// TODO Auto-generated method stub
return 0;
}
}
服装 作为装饰类 Decorator,而且不同的衣服多种多样
package decorator;
public abstract class ClothingDecorator extends Person {
public abstract String getDescription();
}
package decorator;
public abstract class HatDecorator extends Person{
public abstract String getDescription();
}
package decorator;
public class Shirt extends ClothingDecorator{
Person person;
public Shirt(Person person)
{
this.person=person;
}
@Override
public String getDescription() {
// TODO Auto-generated method stub
return person.getDescription()+"a shirt ";
}
@Override
public double cost() {
// TODO Auto-generated method stub
return 100+person.cost();
}
}
package decorator;
public class Hat extends HatDecorator {
Person person;
public Hat(Person person)
{
this.person=person;
}
@Override
public String getDescription() {
// TODO Auto-generated method stub
return person.getDescription()+"a Hat ";
}
@Override
public double cost() {
// TODO Auto-generated method stub
return 50+person.cost();
}
}
客户端代码:
package decorator;
public class Person3 {
public static void main(String[] args) {
Person p1=new Teenager("年轻菜");
p1=new Shirt(p1);
p1=new Hat(p1);
System.out.println(p1.getDescription()+p1.cost());
Person p2=new YoungLady("女仔");
p2=new Shirt(p2);
p2=new Hat(p2);
System.out.println(p2.getDescription()+p2.cost());
}
}
结果:
年轻菜买了:a shirt a Hat 150.0
女仔买了:a shirt a Hat 150.0
UML类图
1、Component
是基类。在上面的例子中,person类则是基类。通常是一个抽象类或者一个接口,定义了属性或者方法,方法的实现可以由子类实现或者自己实现。通常不会直接使用该类,而是通过继承该类来实现特定的功能,它约束了整个继承树的行为。比如说,如果Component代表人,即使通过装饰也不会使人变成别的动物。
2、ConcreteComponent
是Component的子类,实现了相应的方法,它充当了“被装饰者”的角色。 在上面例子中,YoungLady和Teenager 为‘被装饰者’
3、 Decorator
也是Component的子类,它是装饰者共同实现的抽象类(也可以是接口)。比如说,Decorator代表衣服这一类装饰者,那么它的子类应该是T恤、裙子这样的具体的装饰者。 在上面例子中,ClothingDecorator和HatDecorator 则为Decorator
。
4、ConcreteDecorator
是Decorator的子类,是具体的装饰者,由于它同时也是Component的子类,因此它能方便地拓展Component的状态(比如添加新的方法)。每个装饰者都应该有一个实例变量用以保存某个Component的引用,这也是利用了组合的特性。在持有Component的引用后,由于其自身也是Component的子类,那么,相当于ConcreteDecorator包裹了Component,不但有Component的特性,同时自身也可以有别的特性,也就是所谓的装饰。上面例子中,Shirt和Hat 则为ConcreteDecorator
应用
学习了装饰者模式用法、特点以及优缺点后,我们再来看看装饰者模式在实际开发过程的应用。装饰者模式在Java中经常出现的地方就是JavaIO
。提到JavaIO,脑海中就冒出了大量的类:InputStream、FileInputStream、BufferedInputStream……等,真是头都大了,其实,这里面大部分都是装饰类,只要弄清楚这一点就容易理解了。我们来看看JavaIO是怎样使用装饰者模式的。
从字符流来分析,我们知道,有两个基类,分别是InputStream
和OutputStream
,它们也就是我们上面所述的Component基类。接着,它有如下子类:FileInputStream
、StringBufferInputStream
等,它们就代表了上面所述的ConcreteComponent,即装饰对象。此外,InputStream还有FilterInputStream
这个子类,它就是一个抽象装饰者,即Decorator
,那么它的子类:BufferedInputStream
、DataInputStream
等就是具体的装饰者
了。那么,从装饰者模式的角度来看JavaIO,是不是更加容易理解了呢?