行为型设计模式概述
行为模式关注的是对象的行为。该类型的模式需要做的是对可能变化的行为进行抽象,通过封装达到整个架构的可扩展性。这些模式所要封装的行为,恰恰是软件架构中最不稳定的部分,扩展的可能性也最大。将这些行为封装起来,利用抽象的特性,就提供了扩展的可能。
实现的机制:
使用继承机制在类间分派行为
使用对象组合而不是继承,它描述一组对象怎样协作完成单个对象所无法完成的任务
(一)模板方法(TEMPLATE METHOD)模式
问题提出:
通常我们会遇到这样的一个问题:我们知道一个算法或是流程所需的关键步聚,并确定了这些步聚的执行顺序。但是某些步聚的具体实现是未知的,或者是某些步聚的实现与具体的环境相关。
生活场景:
一个简单的订单处理需求:一个客户可以在一个订货单中订购多个货物(也称为订货单项目),货物的销售价是根据货物的进货价进行计算的。有些货物可以打折的,有些是不可以打折的。每一个客户都有一个信用额度,每张订单的总价不能超出该客户的信用额度。
根据上面的业务,我们可以知道处理一个订单需要的步聚:
1. 遍历订货单的订货单项目列表,累加所有货物的总价格(根据订货单项目计算出销售价)
2. 根据客户号获得客户的信用额度
3. 把客户号,订单的总价格,及订单项目列表写入到数据库
但是我们并不能确定怎么计算出货物的销售价,怎样根据客户号获得客户的信用额度及把订单信息写入数据库这些方法的具体实现。
模板方法模式把我们不知道具体实现的步聚封装成抽象方法,提供一些按正确顺序调用它们的具体方法(这些具体方法统称为模板方法),这样构成一个抽象基类。子类通过继承这个抽象基类去实现各个步聚的抽象方法,而工作流程却由父类来控制。
public abstract class AbstractOrder {
public Order placeOrder(int customerId , List orderItemList){
int total = 0;
for(int i = 0; i < orderItemList.size();i++){
OrderItem orderItem = (OrderItem)orderItemList.get(i);
total += getOrderItemPrice(orderItem) * orderItem.getQuantity();
}
if(total > getSpendingLimit(customerId)){
throw new BusinessException("超出信用额度" + getSpendingLimit(customerId));
}
int orderId = saveOrder(customerId, total, orderItemList);
return new OrderImpl(orderId,total);
}
public abstract int getOrderItemPrice(OrderItem orderItem);
public abstract int getSpendingLimit(int customerId);
public abstract int saveOrder(int customerId, int total, List orderItemList);
}
把不变的行为搬到超类,去除子类中重复的代码来体现他的优势。当不变的和可变的行为在方法中混合在一起时,不变的行为就会在子类中重复出现,模板方法模式就是将这些不变的行为搬移到一个超类中,避免重复代码。
这是一个很简单的模式,却被非常广泛的使用。之所以简单是因为在这个模式中仅仅使用到了继承关系。
继承关系由于自身的缺陷,被专家们扣上了“罪恶”的帽子。“使用委派关(聚合)系代替继承关系”,“尽量使用接口实现而不是抽象类继承”等等专家警告,让我们这些菜鸟对继承“另眼相看”。
其实,继承还是有很多自身的优点所在。只是被大家滥用的似乎缺点更加明显了。合理的利用继承关系,还是能对你的系统设计起到很好的作用的。而模板方法模式就是其中的一个使用范例。
(二)策略(Strategy )模式
问题提出:
在业务当中常常出现一个问题有一组算法,在不同的情况下我们有可能使用不同的算法。我们需要找到一种灵活简便的设计方式:将每一个算法封装到具有共同接口的独立的类中,从而使得它们可以相互替换。
生活场景:
向客户报价,对于销售部门的人来讲,这是一个非常复杂的问题,对不同的客户要报不同的价格,比如:
对普通客户或者是新客户报的是全价;
对老客户报的价格,根据客户年限,给予一定的折扣;
对大客户报的价格,根据大客户的累计消费金额,给予一定的折扣;
还要考虑客户购买的数量和金额,比如:虽然是新用户,但是一次购买的数量非常大,或者是总金额非常高,也会有一定的折扣;
还有,报价人员的职务高低,也决定了他是否有权限对价格进行一定的浮动折扣;
总之,向客户报价是非常复杂的,不同的情况销售人员采取不同的策略。
public class Price {
/** @param goodsPrice 商品销售原价
* @param customerType 客户类型
* @return 计算出来的,应该给客户报的价格*/
public double quote(double goodsPrice,String customerType){
if(customerType.equals("普通客户")){
System.out.println("对于新客户或者是普通客户,没有折扣");
return goodsPrice;
}else if(customerType.equals("老客户")){
System.out.println("对于老客户,统一折扣5%");
return goodsPrice*(1-0.05);
}else if(customerType.equals("大客户")){
System.out.println("对于大客户,统一折扣10%");
return goodsPrice*(1-0.1);
}
return goodsPrice;
}
}
价格类包含了所有计算报价的算法,使得价格类,尤其是报价这个方法比较庞杂,难以维护。 咋办?
public class Price {
public double quote(double goodsPrice,String customerType){
if(customerType.equals("普通客户")){
return this.calcPriceForNormal(goodsPrice);
}else if(customerType.equals("老客户")){
return this.calcPriceForOld(goodsPrice);
}else if(customerType.equals("大客户")){
return this.calcPriceForLarge(goodsPrice);
}
return goodsPrice;
}
private double calcPriceForNormal(double goodsPrice){…}
private double calcPriceForOld(double goodsPrice){… }
private double calcPriceForLarge(double goodsPrice){…}
}
再想想,问题还是存在,只不过从计算报价的方法挪动到价格类里面了,假如有100个或者更多这样的计算方式,这会让这个价格类非常庞大,难以维护。而且,维护和扩展都需要去修改已有的代码,这是很不好的,违反了开-闭原则。
另外:经常会有这样的需要,在不同的时候,要使用不同的计算方式。
比如:在公司周年庆的时候,所有的客户额外增加3%的折扣;在换季促销的时候,普通客户是额外增加折扣2%,老客户是额外增加折扣3%,大客户是额外增加折扣5%。这意味着计算报价的方式会经常被修改,或者被切换。过了促销时间,又还回到正常的价格体系上来了。而现在的价格类中计算报价的方法,是固定调用各种计算方式,这使得切换调用不同的计算方式很麻烦,每次都需要修改if-else里面的调用代码。
用来解决上述问题的一个合理的解决方案就是策略模式。
策略模式使得算法可以在不影响到客户端的情况下发生变化。策略模式把行为和环境分开。环境类负责维持和查询行为类,各种算法在具体的策略类中提供。由于算法和环境独立开来,算法的增减,修改都不会影响到环境和客户端。
根据策略模式解决场景问题,设计如下:
public interface Baojia {
public double baojia(double price);
}
public class Content {
private Baojia bj;
public Content(Baojia bj) {
super();
this.bj = bj;
}
public double daZhe(double price){
return bj.baojia(price);
}
}
public class LaoKeHuYear3_5 implements Baojia {
@Override
public double baojia(double price) {
return price*.75;
}
}
public class NewKenHu implements Baojia {
@Override
public double baojia(double price) {
if(price>10000){
return price*(1-0.2);
}
return price;
}
}
public class DaKeHu implements Baojia {
@Override
public double baojia(double price) {
return price*0.5;
}
}
public class Test {
public static void main(String[] args) {
Baojia bj = new LaoKeHuYear3_5();
Content c = new Content(bj);
double p = c.daZhe(1000);
System.out.println(p);
}
}
(三)观察者(Observer )模式
问题提出:
在软件系统中,我们有时需要定义一种一对多的依赖关系. 让多个观察者对象同时监听某一个主题对象, 这个主题对象在状态发生变化的时候,会通知所有的观察者对象,使他们能够自动更新自己。
生活场景:
网上商店中商品在名称 价格等方面有变化,如果系统能自动通知会员,将网上商店区别传统商店的一大特色.
代码实现:定义主题类
public class product extends Observable{
private String name;
private float price;
public String getName(){
return name;
}
public void setName(String name){
this.name=name;
//设置变化点
setChanged();
notifyObservers(name);
}
public float getPrice(){
return price;
}
public void setPrice(float price){
this.price=price;
//设置变化点
setChanged();
notifyObservers(new Float(price));
}
}
代码实现:定义观察者
public class NameObserver implements Observer{
private String name=null;
public void update(Observable obj,Object arg){
if (arg instanceof String){
name=(String)arg;
System.out.println("NameObserver :name changet to "+name);
}
}
}
public class PriceObserver implements Observer{
private float price=0;
public void update(Observable obj,Object arg){
if (arg instanceof Float){
price=((Float)arg).floatValue();
System.out.println("PriceObserver :price changet to "+price);
}
}
}
public class Test {
public static void main(String args[]){
Product product=new Product();
NameObserver nameobs=new NameObserver();
PriceObserver priceobs=new PriceObserver();
//加入观察者
product.addObserver(nameobs);
product.addObserver(priceobs);
product.setName("橘子红了");
product.setPrice(9.22f);
}
}
(四)命令(Command )模式
问题提出:
在软件系统中,“行为请求者”与“行为实现者”通常呈现一种“紧耦合”。但在某些场合,比如要对行为进行“记录、撤销/重做、事务”等处理,这种无法抵御变化的紧耦合是不合适的。在这种情况下,如何将“行为请求者”与“行为实现者”解耦?将一组行为抽象为对象,可以实现二者之间的松耦合。