您的位置:首页 > 编程语言 > Java开发

Java设计模式:三、装饰者模式

2017-05-31 17:00 411 查看
工作一年多了,学习完装饰者模式,然后仔细想了想,好像以前的工作过程中也没有遇到过类似装饰者模式的例子,想找一个实际例子来温习一下,还真难。

偶然想起之前参与过的一个小任务订单活动的任务,我们这边在开发活动规则,另一个同事在通过活动来计算金额。想想,这儿计算金额的例子,倒是可以勉强用用。

大概背景

一个类似淘宝一样的销售系统,商户可以在某一段时间内发起活动吸引顾客购买。活动包括:

- 商品活动 (针对某个商品的活动,比如购买立减xx元,买xx件增y件等等)

- 订单活动(针对一笔订单的活动,例如饿了么满xx元建y元,或增什么什么)

- 券活动(发起活动,顾客可以先领券,然后付款时使用券抵用现金)

在付款时,顾客如果是会员,可以根据会员等级打折,同时如果有足够的积分,可以使用积分抵用现金。

这儿的例子主要是用来计算金额时用到。

假定:

所有商品活动:只要购买,就打9折

订单活动:一笔订单满100元减10元,满200元减25

券活动:一笔订单满300元以上可以使用5元抵用券,满500元以上可以使用20元抵用券

会员享受:会员等级3级以上,打9.5折,会员等级满8级以上,打9折。

假如现在一个4级会员的顾客领取了15元抵用券,购买了300元商品,付款时,实际需要付款多少钱。

不使用模式

/**
* 商品活动
*/
public class GoodsActivity {

private String name;  //活动名
private Double discount;  //折扣价

public static GoodsActivity initGoodsActivity() {
GoodsActivity goodsActivity = new GoodsActivity();
goodsActivity.setName("商品活动");
goodsActivity.setDiscount(0.9);
return goodsActivity;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public Double getDiscount() {
return discount;
}

public void setDiscount(Double discount) {
this.discount = discount;
}
}

/**
* 订单活动
*/
public class OrderActivity {

private String name;
private List<ActivityDetail> detailList;

public static class ActivityDetail {
public BigDecimal  aggregateAmount;    //单笔订单总金额
public BigDecimal reduceAmount;        //满减金额

private ActivityDetail(BigDecimal aggregateAmount, BigDecimal reduceAmount) {
this.aggregateAmount = aggregateAmount;
this.reduceAmount = reduceAmount;
}
}

public static OrderActivity initOrderActivity(){
List<ActivityDetail> orderActivityDetailList = new ArrayList<>();
ActivityDetail orderDetail1 = new ActivityDetail(BigDecimal.valueOf(100), BigDecimal.valueOf(10));
ActivityDetail orderDetail2 = new ActivityDetail(BigDecimal.valueOf(200), BigDecimal.valueOf(25));
orderActivityDetailList.add(orderDetail1);
orderActivityDetailList.add(orderDetail2);

OrderActivity orderActivity = new OrderActivity();
orderActivity.setName("订单活动");
orderActivity.setDetailList(orderActivityDetailList);

return orderActivity;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public List<ActivityDetail> getDetailList() {
return detailList;
}

public void setDetailList(List<ActivityDetail> detailList) {
this.detailList = detailList;
}
}

/**
* 券活动
*/
public class CouponActivity {

private String name;
private List<Detail> detailList;

public static class Detail {
public BigDecimal aggregateAmount;    //订单总金额
public BigDecimal enableUseAmount;        //可使用券

private Detail(BigDecimal aggregateAmount, BigDecimal enableUseAmount) {
this.aggregateAmount = aggregateAmount;
this.enableUseAmount = enableUseAmount;
}
}

public static CouponActivity initCouponActivity() {
List<Detail> detailList = new ArrayList<>();
Detail detail1 = new Detail(BigDecimal.valueOf(300), BigDecimal.valueOf(5));
Detail detail2 = new Detail(BigDecimal.valueOf(500), BigDecimal.valueOf(20));
detailList.add(detail1);
detailList.add(detail2);

CouponActivity couponActivity = new CouponActivity();
couponActivity.setName("订单活动");
couponActivity.setDetailList(detailList);

return couponActivity;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public List<Detail> getDetailList() {
return detailList;
}

public void setDetailList(List<Detail> detailList) {
this.detailList = detailList;
}
}

/**
* 会员信息
*/
public class Memory {

private int grade;    //会员等级
private long points;  //账户含有积分

public static Memory initMemory() {
Memory memory = new Memory();
memory.setGrade(4);
memory.setPoints(500);
return memory;
}

public int getGrade() {
return grade;
}

public void setGrade(int grade) {
this.grade = grade;
}

public long getPoints() {
return points;
}

public void setPoints(long points) {
this.points = points;
}
}

/**
* 实付金额计算测试
*/
public class PayTest {

public static void main(String[] args) {

//初始化活动
GoodsActivity goodsActivity = GoodsActivity.initGoodsActivity();
OrderActivity orderActivity = OrderActivity.initOrderActivity();
CouponActivity couponActivity = CouponActivity.initCouponActivity();
Memory memory = Memory.initMemory();

//购买了300元的商品
BigDecimal purchaseAmount = BigDecimal.valueOf(300);

//计算商品折扣
Double goodsActivityDiscount = goodsActivity.getDiscount();

//计算订单减价
BigDecimal orderReduceAmount = BigDecimal.ZERO;
List<OrderActivity.ActivityDetail> detailList = orderActivity.getDetailList();
for (OrderActivity.ActivityDetail detail : detailList) {
if (purchaseAmount.compareTo(detail.aggregateAmount) >= 0) {
orderReduceAmount = detail.reduceAmount;
}
}

//计算可使用券金额
BigDecimal couponReduceAmount = BigDecimal.ZERO;
List<CouponActivity.Detail> couponActivityDetailList = couponActivity.getDetailList();
for (CouponActivity.Detail detail : couponActivityDetailList) {
if (purchaseAmount.compareTo(detail.aggregateAmount) >= 0) {
couponReduceAmount = detail.enableUseAmount;
}
}

//计算会员折扣
double memoryDiscount = 1;
if (memory.getGrade() > 3 && memory.getGrade() < 8) {
memoryDiscount = 0.95;
} else if (memory.getGrade() > 8) {
memoryDiscount = 0.9;
}

//计算实付金额
BigDecimal actualAmount = purchaseAmount.subtract(orderReduceAmount)
.subtract(couponReduceAmount)
.multiply(BigDecimal.valueOf(goodsActivityDiscount))
.multiply(BigDecimal.valueOf(memoryDiscount));

System.out.println(actualAmount.toString());
}

}

最后输出:
230.850


上面这个是不使用模式的时候。看到测试类中的一大堆代码,阅读起来就麻烦,何况后期的维护呢。一旦增加一个新的需求,或者说现在少了一个活动,这个改动会很大。

但是如果使用装饰者模式,就不一样了。

装饰者模式

动态的将责任添加到对象上。对象和装饰者具有相同的类型。

如果将一笔订单设计为一个对象,计算实付金额的其他类,就是用来装饰这个订单对象,以达到最终的金额。

装饰者模式的类图:



使用装饰者模式

因为此例只有一种order,所以,代码为右边这条线为主。

/**
* 订单基类
**/
public class Order {

private BigDecimal allamount;   //订单总额

//初始化一个订单
public static Order initOrder(){
Order order = new Order();
order.setAllamount(BigDecimal.valueOf(300));
return order;
}

public BigDecimal getAllamount() {
return allamount;
}

public void setAllamount(BigDecimal allamount) {
this.allamount = allamount;
}

public BigDecimal getAmount() {
return getAllamount();
}
}

/**
* 商品活动
**/
public class GoodsActivity extends Order {

private Order order;
private String name;  //活动名
private Double discount;  //折扣价

public GoodsActivity(Order order) {
this.order = order;
}

public GoodsActivity() {
}

//初始化商品活动
public static GoodsActivity initGoodsActivity() {
GoodsActivity goodsActivity = new GoodsActivity();
goodsActivity.setName("商品活动");
goodsActivity.setDiscount(0.9);
return goodsActivity;
}

@Override
public BigDecimal getAmount() {
return order.getAmount().multiply(BigDecimal.valueOf(initGoodsActivity().getDiscount()));
}

public void setName(String name) {
this.name = name;
}

public Double getDiscount() {
return discount;
}

public void setDiscount(Double discount) {
this.discount = discount;
}
}

/**
* 订单活动
*/
public class OrderActivity extends Order{

private Order order;
private String name;
private List<ActivityDetail> detailList;

public static class ActivityDetail {
public BigDecimal  aggregateAmount;    //单笔订单总金额
public BigDecimal reduceAmount;        //满减金额

private ActivityDetail(BigDecimal aggregateAmount, BigDecimal reduceAmount) {
this.aggregateAmount = aggregateAmount;
this.reduceAmount = reduceAmount;
}
}

public OrderActivity(Order order) {
this.order = order;
this.setAllamount(order.getAllamount());
}

public OrderActivity() {
}

public static OrderActivity initOrderActivity(){
List<ActivityDetail> orderActivityDetailList = new ArrayList<>();
ActivityDetail orderDetail1 = new ActivityDetail(BigDecimal.valueOf(100), BigDecimal.valueOf(10));
ActivityDetail orderDetail2 = new ActivityDetail(BigDecimal.valueOf(200), BigDecimal.valueOf(25));
orderActivityDetailList.add(orderDetail1);
orderActivityDetailList.add(orderDetail2);

OrderActivity orderActivity = new OrderActivity();
orderActivity.setName("订单活动");
orderActivity.setDetailList(orderActivityDetailList);

return orderActivity;
}

@Override
public BigDecimal getAmount() {
BigDecimal orderReduceAmount = BigDecimal.ZERO;
List<ActivityDetail> detailList = initOrderActivity().getDetailList();
for (ActivityDetail detail : detailList) {
if (order.getAllamount().compareTo(detail.aggregateAmount) >= 0) {
orderReduceAmount = detail.reduceAmount;
}
}
return order.getAmount().subtract(orderReduceAmount);
}

public Order getOrder() {
return order;
}

public void setOrder(Order order) {
this.order = order;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public List<ActivityDetail> getDetailList() {
return detailList;
}

public void setDetailList(List<ActivityDetail> detailList) {
this.detailList = detailList;
}
}

/**
* 券活动
*/
public class CouponActivity  extends Order{

private Order order;
private String name;
private List<Detail> detailList;

public static class Detail {
public BigDecimal aggregateAmount;    //订单总金额
public BigDecimal enableUseAmount;        //可使用券

private Detail(BigDecimal aggregateAmount, BigDecimal enableUseAmount) {
this.aggregateAmount = aggregateAmount;
this.enableUseAmount = enableUseAmount;
}
}

public CouponActivity() {
}

public CouponActivity(Order order) {
this.order = order;
this.setAllamount(order.getAllamount());
}

public static CouponActivity initCouponActivity() {
List<Detail> detailList = new ArrayList<>();
Detail detail1 = new Detail(BigDecimal.valueOf(300), BigDecimal.valueOf(5));
Detail detail2 = new Detail(BigDecimal.valueOf(500), BigDecimal.valueOf(20));
detailList.add(detail1);
detailList.add(detail2);

CouponActivity couponActivity = new CouponActivity();
couponActivity.setName("订单活动");
couponActivity.setDetailList(detailList);

return couponActivity;
}

@Override
public BigDecimal getAmount() {
BigDecimal couponReduceAmount = BigDecimal.ZERO;
List<Detail> couponActivityDetailList = initCouponActivity().getDetailList();
for (Detail detail : couponActivityDetailList) {
if (order.getAllamount().compareTo(detail.aggregateAmount) >= 0) {
couponReduceAmount = detail.enableUseAmount;
}
}
return order.getAmount().subtract(couponReduceAmount);
}

public Order getOrder() {
return order;
}

public void setOrder(Order order) {
this.order = order;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public List<Detail> getDetailList() {
return detailList;
}

public void setDetailList(List<Detail> detailList) {
this.detailList = detailList;
}
}

//会员类保持不变

/**
* 测试代码
**/
public class PayTest {

public static void main(String[] args) {
//计算会员折扣
Memory memory = Memory.initMemory();
double memoryDiscount = 1;
if (memory.getGrade() > 3 && memory.getGrade() < 8) {
memoryDiscount = 0.95;
} else if (memory.getGrade() > 8) {
memoryDiscount = 0.9;
}
Order order = Order.initOrder();

order = new OrderActivity(order);
order = new CouponActivity(order);
order =  new GoodsActivity(order);

System.out.println(order.getAmount().multiply(BigDecimal.valueOf(memoryDiscount)));
}
}

输出:
230.850


上面的这里面有很多init方法,这个方法的主要目的是用来初始化一个活动规则,因为价格的计算是依赖这个规则的。还有一些冗余字段,比如name是没有用到的。

也许这个例子举得不好吧。下面是装饰者模式的对象结构。



它的结构是根据new的顺序来创建的。

先创建一个Order对象

然后再创建一个OrderActivity对象,这个对象包含Order对象,也就是说,OrderActivity对象装饰了Order对象。以此类推下去。

它的getAmount()方法的调用顺序,也是如此。下图可以表示:



使用装饰者模式和不适用装饰者模式的主要区别是,使用装饰者模式可以很好的去扩展类,而原始的这种方式,一旦加入新的一种计算方式,势必要去修改代码。而设计原则中,对修改关闭,对扩展开放。就是说,尽量不要去修改以前写好的代码。所以使用装饰者模式,只需要再增加一个类即可,不需要去关心客户端怎么计算。

总结

装饰者模式的意图是动态的将责任增加到原对象上。所以,每一个装饰者都和原对象具有相同的类型。需要注意的就是每一个装饰者中的原对象(示例中的Order对象),始终都是同一个。

- 装饰者和被装饰者具有相同的超类型

- 可以用一个或多个装饰者包装一个对象

- 装饰者可以在所委托被装饰者的行为之前/之后加上自己的行为,以达到特定的目的

- 对象可以在任何时候被装饰,可以在运行时动态的不限量的指定装饰者来修饰它。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: