Java设计模式笔记之装饰模式

1.装饰模式

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


2.模式结构


装饰模式中的角色:

抽象构件(Component):一个抽象对象,定义被装饰构件的行为;

具体构件(ConcreteComponent):被装饰的类,需要添加功能的类;

抽象装饰角色(Decorator):持有一个Component实例(被装饰的对象),并且实现Component的行为,保持装饰与具体构件接口一致。

具体装饰角色(ConcreteDecorator):具体给构件添加功能的类。


3.装饰模式适用场景

(1)需要扩展一个类的功能,或者给一个类增加附加责任;

(2)需要动态的给一个对象增加功能,这些功能可以再动态地撤销;

(3)需要增加由一些基本功能的排列组合而产生的非常大量的功能,这是使用继承关系不太现实。


4.发票系统例子

需求:

一个电子销售系统需要打印客户所购买的商品发票.一章发票分为3部分

(1)发票头部,包括客户的名字和销售日期

(2)发票主体,购买的货物清单

(3)发票尾部,总金额


头部和尾部有不同的格式,因此需要灵活的为发票添加头部和尾部。可以使用装饰模式。

类图:


SalesOrder 是发票主体,持有一定数量的OrderLine;

OrderLine是发票清单中的一行,包括品名、单价、数量等

OrderDecorator抽象的发票装饰类;

HeaderDecorator 和FooterDecorator:具体装饰类,分别是发票头部和发票尾部,如果有其他格式的头部或者尾部,可以继承OrderDecorator实现。

源码:

Order.java

package com.patterns.decorder;

import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

abstract public class Order
{
    
    protected String customerName;
    protected Date salesDate;
	protected List<OrderLine> items = new ArrayList<OrderLine>();

    public void print()
    {
		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 remoteItem(OrderLine item)
    {
        items.remove(item);
    }

    public double getGrandTotal()
    {
        double amnt = 0.0D;

		for (int i = 0 ; i < items.size() ; i++)
        {
			OrderLine item = (OrderLine) items.get(i);
            amnt += item.getSubtotal();
        }

        return amnt;
    }

    protected String formatCurrency(double amnt)
    {
        return NumberFormat.getCurrencyInstance().format(amnt);
    }

}

OrderLine.java

package com.patterns.decorder;

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" + formatCurrency(unitPrice)
            + "\t" + formatCurrency(getSubtotal()));
    }

    public double getSubtotal()
    {
        return unitPrice * units;
    }

    private String formatCurrency(double amnt)
    {
        return NumberFormat.getCurrencyInstance().format(amnt);
    }
}

OrderDecorator.java

package com.patterns.decorder;

abstract public class OrderDecorator extends Order
{
    /**
     * @link aggregation
     * @directed
     * @supplierCardinality 1
     * @clientCardinality 0..1
     */
    protected Order order;

    public OrderDecorator(Order order)
    {
        this.order = order;
        this.setSalesDate( order.getSalesDate() );
        this.setCustomerName( order.getCustomerName() );
    }

    public void print()
    {
		super.print();
    }
}

FooterDecorator.java

package com.patterns.decorder;

public class FooterDecorator extends OrderDecorator
{
    public FooterDecorator(Order anOrder)
    {
        super(anOrder);
    }

    public void print()
    {
        super.order.print();
        printFooter();
    }

    private void printFooter()
    {

        System.out.println("========================================================");
        System.out.println("Total\t\t\t\t" +
            formatCurrency(super.order.getGrandTotal()));
    }

}

HeaderDecorator.java

public class HeaderDecorator extends OrderDecorator
{
    public HeaderDecorator(Order anOrder)
    {
        super(anOrder);
    }

    public void print()
    {
        this.printHeader();
        super.order.print();
    }

    private void printHeader()
    {
        System.out.println("\t***\tI N V O I C E\t***\nXYZ Incorporated\nDate of Sale: "
            + order.getSalesDate());
        System.out.println("========================================================");
        System.out.println("Item\t\tUnits\tUnit Price\tSubtotal");
    }
}
Client.java(调用代码)

package com.patterns.decorder;

import java.util.Date;

public class Client {
    /**
     * @directed 
     */
    private static Order order;

    public static void main(String[] args)
    {
        order = new SalesOrder();
		order.setSalesDate(new Date());
        order.setCustomerName("XYZ Repair Shop");

        OrderLine line1 = new OrderLine();

        line1.setItemName("FireWheel Tire");
        line1.setUnitPrice(154.23);
        line1.setUnits(4);

        order.addItem(line1);

        OrderLine line2 = new OrderLine();

        line2.setItemName("Front Fender");
        line2.setUnitPrice(300.45);
        line2.setUnits(1);

        order.addItem(line2);

        order = new HeaderDecorator(new FooterDecorator(order));

        order.print();
    }
}

5.装饰模式的优缺点

优点:

(1)装饰模式与继承都可以扩展对象的功能,但是装饰模式更加灵活。而且装饰模式可以动态的添加与去除功能,而继承则是系统运行前定义好的。

(2)不同装饰类对象的排列组合,更加多样化的功能扩展。

缺点:

会使系统产生比使用继承更多的对象。




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值