实例探索Java模式之路——装饰模式

装饰模式


1、装饰模式又名包装模式,是以对客户透明的方式扩展对象的功能,是继承关系的一个替代方案。


2、装饰模式结构:


抽象构件角色:给出一个抽象接口,以规范准备接收附加任务的对象。
具体构件角色:定义一个将要接收附加责任的类。
装饰角色:持有一个构件对象的实例,并定义一个与抽象构件接口一致的接口。
具体装饰角色:负责给构件对象附加上附加责任。


3、一个例子:


//抽象构件
public interface Component {


// 某个商业方法
void sampleOperation();


}




//具体构件
public class ConcreteComponent implements Component {


// 无参构造
public ConcreteComponent() {
super();
System.out.println("具体构件无参构造");
}


@Override
public void sampleOperation() {
System.out.println("具体构件商业方法");
}


}




//装饰角色
public class Decorator implements Component {
private Component component;


// 有参构造
public Decorator(Component component) {
this.component = component;
System.out.println("装饰有参构造");
}


// 无参构造
public Decorator() {
super();
System.out.println("装饰无参构造");
}


// 商业方法,委派给构件
@Override
public void sampleOperation() {
component.sampleOperation();
System.out.println("装饰商业方法");
}


}




//具体装饰1
public class ConcreteDecorator1 extends Decorator {


// 无参构造
public ConcreteDecorator1() {
System.out.println("具体装饰1无参构造");
}


public ConcreteDecorator1(Component component) {
super(component);
}


// 商业方法
@Override
public void sampleOperation() {


super.sampleOperation();
redecorator();
System.out.println("具体装饰1商业方法");
}


private void redecorator() {
System.out.println("具体装饰1包装一次");
}


}




//具体装饰2
public class ConcreteDecorator2 extends Decorator {


public ConcreteDecorator2(Component component) {
super(component);
}


// 商业方法
@Override
public void sampleOperation() {


super.sampleOperation();
redecorator();
System.out.println("具体装饰2商业方法");
}


private void redecorator() {
System.out.println("具体装饰2包装一次");
}


}



public class client {
public static void main(String[] args) {


ConcreteComponent component = new ConcreteComponent();


// 包装一次
ConcreteDecorator1 cd1 = new ConcreteDecorator1(component);
// 包装二次
ConcreteDecorator2 cd2 = new ConcreteDecorator2(cd1);


cd2.sampleOperation();


System.out.println();


}
}


在上面例子中cd2的内部结构:



结果打印:
具体构件无参构造
装饰有参构造
装饰有参构造
具体构件商业方法
装饰商业方法
具体装饰1包装一次
具体装饰1商业方法
装饰商业方法
具体装饰2包装一次
具体装饰2商业方法




4、使用场景


1、需要扩展一个类的功能,或者为一个类增加附加责任;
2、需要动态地给一个对象增加功能,这些功能可以再动态撤销;
3、需要增加由一些基本功能的排列组合而产生非常大量的功能,继承关系行不通。


5、一个猴王的例子:


//抽象构件,定义移动方法
public interface TheGreateSage {


public void move();
}


//具体构件
public class Monkey implements TheGreateSage {


@Override
public void move() {
System.out.println("猴子的移动方法!");
}


}




//装饰角色
public class Changes implements TheGreateSage {


// 委派
private TheGreateSage thegreatesage;


// 有参构造
public Changes(TheGreateSage thegreatesage) {
super();
this.thegreatesage = thegreatesage;
}


@Override
public void move() {
thegreatesage.move();
}
}




//具体装饰角色,鱼儿
public class Fish extends Changes {


public Fish(TheGreateSage thegreatesage) {
super(thegreatesage);
}


// 扩展功能
public void move() {
System.out.println("鱼儿在游泳!");
}


// 扩展功能
public void swim() {
System.out.println("鱼儿游泳方法!");
}
}






//具体装饰角色,鸟儿
public class Bird extends Changes {


public Bird(TheGreateSage thegreatesage) {
super(thegreatesage);
}


// 扩展功能
public void move() {
System.out.println("鸟儿在飞翔!");
}


// 扩展功能
public void fly() {
System.out.println("鸟儿飞翔方法!");
}
}






/*客户端角色,对于客户端而言,看到的依然是猴子的,客户端并不知道,进过装饰类的
的包装,猴子已经具有了鸟儿和鱼儿功能,所以对于客户端这种扩展是透明的。*/
public class client {
public static void main(String[] args) {
TheGreateSage sage = new Monkey();


// 第一次包装,猴王具有了鱼儿的功能
TheGreateSage fish = new Fish(sage);


fish.move();


// 第二次包装,猴王具有了鱼儿的功能以及鸟儿的功能
TheGreateSage bird = new Bird(fish);
// 另一种写法
TheGreateSage bird1 = new Bird(new Fish(sage));


bird.move();
bird1.move();


/*
* 在具体装饰类中,鸟儿和鱼儿都由自己的方法,因为在Component里面没有 这些方法,所以透明的包装是客户无法调用这些方法
* fish.swim();显然会报错。 纯粹的装饰模式很难找到,装饰模式的用意是不改变接口的前提下,增强
* 所考虑的类的性能,在增强性能时往往需要建立新的公开的方法。这导致大多数
* 的装饰模式是半透明的。换言之是装饰模式允许改变接口,增加新的方法。客户端可以声明 具体装饰类型的变量,从而可以调用具体装饰类才有的方法。
*/


Fish fish1 = new Fish(sage);// 半透明模式
fish1.swim();// 可以调到具体装饰类的独有方法。
Bird bird2 = new Bird(fish);
bird2.fly();



/*
* 只要客户端在不需要调用这些属于装饰的方法,而值调用Component的方法 装饰模式依然等同于透明的。
*/


}
}


6、装饰模式与适配器模式关系:
半透明的装饰模式是介于装饰模式和适配器模式之间的方法,适配器模式的用意是改变所考虑类的接口,也可以通过 改写一个或者几个方法,或者增加新的方法来增强或改变所考虑类的功能。
大多是的装饰模式实际是半透明的装饰模式,这样的模式也叫半装饰模式、半适配器模式。


7、一个打发票的例子(加深理解):


import java.text.NumberFormat;
import java.util.Date;
import java.util.Vector;


//抽象构件角色
public abstract class Order {


private OrderLine lnkOrderLine;
protected String customerName;// 客户名
protected Date salesDate;// 销售日期
protected Vector items = new Vector(10);// Vector聚集,存储任意多个OrderLine对象


//类似上面代码的商业方法
public void print() {
System.out.println("抽象构件打印");
for (int i = 0; i < items.size(); i++) {
OrderLine item = (OrderLine) items.get(i);
item.printLine();
}
}


public String getCustomerName() {
return customerName;
}


public void setCustomerName(String customerName) {
this.customerName = customerName;
}


public Date getSalesDate() {
return salesDate;
}


public void setSalesDate(Date salesDate) {
this.salesDate = salesDate;
}


// 增加一行销售产品
public void addItem(OrderLine item) {
items.add(item);
}


// 删除一行销售产品


public void remove(OrderLine item) {
items.remove(item);
}


// 得到总计金额
public double getGarntTotal() {
double amnt = 0.0D;
for (int i = 0; i < items.size(); i++) {
OrderLine item = (OrderLine) items.get(i);
amnt += item.getSubtotal();
}
return amnt;
}


// 金额格式化
private String formateCurrency(double amnt) {
return NumberFormat.getCurrencyInstance().format(amnt);
}
}




import java.text.NumberFormat;
//商品实体类
//发票的货品清单的一行,给出产品名,单价,购买数量,小计金额等。


public class OrderLine {
private String itemName;// 产品名
private int units;// 单位数量
private double unitPrice;// 单价


public String getItemName() {
return itemName;
}


public void setItemName(String itemName) {
this.itemName = itemName;
}


public int getUnits() {
return units;
}


public void setUnits(int units) {
this.units = units;
}


public double getUnitPrice() {
return unitPrice;
}


public void setUnitPrice(double unitPrice) {
this.unitPrice = unitPrice;
}


public void printLine() {
System.out.println(itemName + "\t" + units + "\t"
+ formateCurrency(unitPrice) + "\t"
+ formateCurrency(getSubtotal()));


}


// 金额格式化
private String formateCurrency(double amnt) {
return NumberFormat.getCurrencyInstance().format(amnt);
}


// 小计金额取值
public double getSubtotal() {
return unitPrice * units;
}
}




//具体构件类SalesOrder,发票的主部
public class SalesOrder extends Order {


// 无参构造方法
public SalesOrder() {
System.out.println("发票的主部无参构造方法");
}


public void print() {
System.out.println("发票的主部打印方法");
super.print();// 调用父类打印
}
}




//抽象装饰角色 OrderDecorator
public abstract class OrderDecorator extends Order {


// 依赖于被装饰的对象
protected Order order;


// 有参构造
public OrderDecorator(Order order) {
this.order = order;
this.setSalesDate(order.getSalesDate());
this.setCustomerName(order.getCustomerName());
}


public void print() {
System.out.println("抽象装饰打印");
super.print();
}
}




//抽象装饰角色 OrderDecorator
public abstract class OrderDecorator extends Order {


// 依赖于被装饰的对象
protected Order order;


// 有参构造
public OrderDecorator(Order order) {
this.order = order;
this.setSalesDate(order.getSalesDate());
this.setCustomerName(order.getCustomerName());
}


public void print() {
System.out.println("抽象装饰打印");
super.print();
}
}




//具体装饰角色,发票头部
public class HeaderDecorator1 extends OrderDecorator {


// 构造方法
public HeaderDecorator1(Order order) {
super(order);
}


public void print() {
this.printHeader();// 打印发票头部
super.order.print();// 调用被装饰对象的打印功能
}


//扩展功能
private void printHeader() {
System.out.println("\t**\t发票(另外一种头部)\t**");
System.out.println("销售公司");
System.out.println(order.getCustomerName());
System.out.println("===============");
System.out.println("产品名\t数量\t单价\t小计金额");
}
}




import java.text.NumberFormat;


//具体装饰角色,发票尾部
public class FooterDecorator extends OrderDecorator {


public FooterDecorator(Order order) {
super(order);
}


public void print() {
super.order.print();// 调用被装饰对象的打印功能
this.printFooter();// 打印发票尾部
}


private void printFooter() {
System.out.println("===============");


System.out.println("总额:\t\t\t\t"
+ formateCurrency(super.order.getGarntTotal()));// super.order.getGarntTotal()是取父类的父类的getGarntTotal()方法
}


// 金额格式化
private String formateCurrency(double amnt) {
return NumberFormat.getCurrencyInstance().format(amnt);
}
}




import java.util.Date;


/*客户端角色,*/
public class client {
private static Order order, order1 ,footerdecorator;


public static void main(String[] args) {


order = new SalesOrder();
order.setSalesDate(new Date());
order.setCustomerName("汽车店");
OrderLine line1 = new OrderLine();
line1.setItemName("四个轮胎");
line1.setUnitPrice(200);
line1.setUnits(4);
order.addItem(line1);


OrderLine line2 = new OrderLine();
line2.setItemName("车座");
line2.setUnitPrice(1000);
line2.setUnits(1);
order.addItem(line2);



footerdecorator =new FooterDecorator(order);

order = new HeaderDecorator(footerdecorator);// 经过2层包装


order.print();


System.out.println("=============第二种方式===============");
/*
* 关于order.print(); 
* 最先调用的是具体装饰角色,发票头部的打印方法,因为头部包装在最外面,然后调用头部的私有方法(扩展功能),
* 然后调用尾部的打印功能,在调用是又先调用了被装饰对象的打印功能,调用抽象构件的打印方法,调用尾部打印(扩展功能)。
*/


// 采用另外一种头部进行包装
order1 = new HeaderDecorator1(footerdecorator);// 经过2层包装


order1.print();


/*在该实例中,具体装饰类通过把私有的扩展方法放到抽象构件的方法中的方式实现了透明化
也即:
public void print() {
this.printHeader();// 打印发票头部
super.order.print();// 调用被装饰对象的打印功能
}
否则,如果要调用具体装饰类的私有方法,就要通过:
FooterDecorator footerdecorator =new FooterDecorator(order);
HeaderDecorator1 headerdecorator1 = new HeaderDecorator1(footerdecorator);
的方式实现包装,调用具体装饰的私有方法。*/
}
}


发票例子的内部结构:



输出打印:

发票的主部无参构造方法
** 发票 **
销售日期
Thu Jun 15 09:11:09 GMT+08:00 2017
===============
产品名 数量 单价 小计金额
发票的主部打印方法
抽象构件打印
四个轮胎 4 ¥200.00 ¥800.00
车座 1 ¥1,000.00 ¥1,000.00
===============
总额: ¥1,800.00
=============第二种方式===============
** 发票(另外一种头部) **
销售公司
汽车店
===============
产品名 数量 单价 小计金额
发票的主部打印方法
抽象构件打印
四个轮胎 4 ¥200.00 ¥800.00
车座 1 ¥1,000.00 ¥1,000.00
===============
总额: ¥1,800.00



通过此实例,相信对该模式有了进一步的认识。

每天努力一点,每天都在进步。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

powerfuler

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值