设计模式(21)- 装饰者模式

装饰者模式

1.定义

      动态的给一个对象添加一些额外的职责。就增加功能来说,装饰者模式比生成子类更为灵活。

2.示例代码

     以计算奖金为例,说明装饰者模式设计思路。

   

/** 在内存中模拟数据库,准备点测试数据,好计算奖金*/
public class TempDB {
    private TempDB(){ 	
    }
    /* 记录每个人的月度销售额,只用了人员,月份没有用*/
    public static Map<String,Double> mapMonthSaleMoney = new HashMap<String,Double>();
    static{
       //填充测试数据
       mapMonthSaleMoney.put("张三",10000.0);
       mapMonthSaleMoney.put("李四",20000.0);
       mapMonthSaleMoney.put("王五",30000.0);
    }
}
/*组件对象的接口,可以给这些对象动态的添加职责*/
public abstract class Component {
    /**
     * 计算某人在某段时间内的奖金,有些参数在演示中并不会使用,
     * 但是在实际业务实现上是会用的,为了表示这是个具体的业务方法,
     * 因此这些参数被保留了
     * @param user 被计算奖金的人员
     * @param begin 计算奖金的开始时间
     * @param end 计算奖金的结束时间
     * @return 某人在某段时间内的奖金
     */
    public abstract double calcPrize(String user,Date begin,Date end);
}

/* 基本的实现计算奖金的类,也是被装饰器装饰的对象*/
public class ConcreteComponent extends Component{
    public double calcPrize(String user, Date begin, Date end) {
       //只是一个默认的实现,默认没有奖金
       return 0;
    }
}

/* 装饰器的接口,需要跟被装饰的对象实现同样的接口*/
public abstract class Decorator extends Component{
    /* 持有被装饰的组件对象*/
    protected Component c;
    /*通过构造方法传入被装饰的对象*/
    public Decorator(Component c){
       this.c = c;
    }
    public double calcPrize(String user, Date begin, Date end) {
       //转调组件对象的方法
       return c.calcPrize(user, begin, end);
    }
}

/*装饰器对象,计算当月业务奖金*/
public class MonthPrizeDecorator extends Decorator{
    public MonthPrizeDecorator(Component c){
       super(c);
    }  
    public double calcPrize(String user, Date begin, Date end) {
       //1:先获取前面运算出来的奖金
       double money = super.calcPrize(user, begin, end);
       //2:然后计算当月业务奖金,按人员和时间去获取当月业务额,然后再乘以3%
       double prize = TempDB.mapMonthSaleMoney.get(user) * 0.03;
       System.out.println(user+"当月业务奖金"+prize);
       return money + prize;
    }
}

/*装饰器对象,计算累计奖金*/
public class SumPrizeDecorator extends Decorator{
    public SumPrizeDecorator(Component c){
       super(c);
    }  
    public double calcPrize(String user, Date begin, Date end) {
       //1:先获取前面运算出来的奖金
       double money = super.calcPrize(user, begin, end);
       //2:然后计算累计奖金,其实应按人员去获取累计的业务额,然后再乘以0.1%
       //简单演示一下,假定大家的累计业务额都是1000000元
       double prize = 1000000 * 0.001;
       System.out.println(user+"累计奖金"+prize);
       return money + prize;
    }
}

/* 装饰器对象,计算当月团队业务奖金*/
public class GroupPrizeDecorator extends Decorator{
    public GroupPrizeDecorator(Component c){
       super(c);
    }
    public double calcPrize(String user, Date begin, Date end) {
       //1:先获取前面运算出来的奖金
       double money = super.calcPrize(user, begin, end);
       //2:然后计算当月团队业务奖金,先计算出团队总的业务额,然后再乘以1%
       //假设都是一个团队的
       double group = 0.0;
       for(double d : TempDB.mapMonthSaleMoney.values()){
           group += d;
       }
       double prize = group * 0.01;
       System.out.println(user+"当月团队业务奖金"+prize);
       return money + prize;
    }
}

  

/* 使用装饰模式的客户端*/
public class Client {
    public static void main(String[] args) {
       //先创建计算基本奖金的类,这也是被装饰的对象
       Component c1 = new ConcreteComponent();      
       //然后对计算的基本奖金进行装饰,这里要组合各个装饰
       //说明,各个装饰者之间最好是不要有先后顺序的限制,
       //也就是先装饰谁和后装饰谁都应该是一样的    
       //先组合普通业务人员的奖金计算
       Decorator d1 = new MonthPrizeDecorator(c1);
       Decorator d2 = new SumPrizeDecorator(d1);
       //注意:这里只需使用最后组合好的对象调用业务方法即可,会依次调用回去
       //日期对象都没有用上,所以传null就可以了
       double zs = d2.calcPrize("张三",null,null);      
       System.out.println("==========张三应得奖金:"+zs);
       double ls = d2.calcPrize("李四",null,null);
       System.out.println("==========李四应得奖金:"+ls);      
       //如果是业务经理,还需要一个计算团队的奖金计算
       Decorator d3 = new GroupPrizeDecorator(d2);
       double ww = d3.calcPrize("王五",null,null);
       System.out.println("==========王经理应得奖金:"+ww);
    }
}

3.实际应用

       装饰器模式实现了对被装饰对象的某些装饰功能,比对象继承有更好的灵活性,可以在装饰器中调用被装饰对象的功能,获取相应的值,这其实是一种递归调用。各个装饰器之间最好是完全独立的功能,不要有依赖,这样在进行装饰组合的时候,才没有先后顺序的限制,java中最典型的应用就是I/O流。

装饰者模式的本质:动态组合

猜你喜欢

转载自renhanxiang.iteye.com/blog/2408379