设计模式讲解与代码实践(十八)——中介者
2017-08-17 23:04
627 查看
本文来自李明子csdn博客(http://blog.csdn.net/free1985),商业转载请联系博主获得授权,非商业转载请注明出处!
通常来说,交互对象与中介者间的关系可以形象的描述为消息接收与处理。从应用场景来说,中介者对接收到的消息有两种处理方式。第一种是转发消息,即中介者将收到的消息向所有与其相关的同事广播,此时中介者只起到消息转发和信息汇总的作用;第二种是处理消息,即中介者将消息选择性的发送给符合条件的同事,直接参与到业务逻辑处理。这种情况下,代码复杂度将进一步向中介者集中,中介者需要知道同事的具体类型,并了解各具体同事类的业务逻辑。本文中给出的示例采用第一种方式。
图2-1 中介者类图
3.1 Mediator
Mediator是中介者接口,声明了中介者向同事开放的各“事件通知”接口方法。Mediator内部维护了同事接口类型的对象集合,并提供了向中介者添加及移除同事的方法。
3.2 ConcreteMediator
ConcreteMediator是具体中介者,实现了Mediator接口。在实现“事件通知”接口方法时,ConcreteMediator根据业务逻辑调用相应同事对象的对应方法。
3.3 Colleegue
Colleegue是同事接口,内部维护了中介者接口Mediator的实例。另外,如果采用消息转发的形式使用中介者,那么Colleegue还声明了各消息处理接口方法。
3.4 ConcreteColleegue
ConcreteColleegue1和ConcreteColleegue2是具体同事类,实现了同事接口Colleegue。如果采用消息转发的形式使用中介者,在实现同事接口时,具体同事类会忽略对“不感兴趣”消息的处理。
4.1 场景介绍
某电商平台后台拥有多个仓库,支持多用户在线将商品加入购物车及结账购买购物车中的商品。
以下各节将介绍该场景各类的具体实现及其在中介者设计模式中所对应的参与者角色。
4.2 InvChgMediator
InvChgMediator是库存改变中介者类。InvChgMediator声明了维护同事列表的相应方法及库存改变相关的事件通知接口。对应于中介者模式的参与者,InvChgMediator是中介者接口Mediator。下面的代码给出了InvChgMediator的声明。
上述代码中,14行,维护了库存改变同事集合invChgColleegues,41行和49行分别声明了添加同事方法addColleegue和移除同事方法removeColleegue。21行、28行和34行,声明了三个与产品库存相关的抽象方法。
4.3 InvChgMediatorImpl
InvChgMediatorImpl是库存改变中介者实现类,派生于库存改变中介者抽象类InvChgMediator。InvChgMediatorImpl实现了各库存相关方法。对应于中介者模式的参与者,InvChgMediatorImpl是具体中介者类ConcreteMediator。下面的代码给出了InvChgMediatorImpl的声明。
上述代码中,15行,获取指定产品的库存余量方法getTotal,通过遍历同事集合,累加各同事持有的产品数量得到产品余量。31行,产品被消费方法productConsumed,在库存充足的情况下,遍历同事集合,执行出库操作,直至出库数量达到消费数量。51行,库存改变消息处理方法inventoryChg,遍历同事集合,向各同事广播该消息。
4.4 InvChgColleegue
InvChgColleegue是库存改变同事类。InvChgColleegue是抽象类,声明了库存相关的方法并提供了默认实现。因本例中采用的是消息转发的形式,故InvChgColleegue声明的是库存相关消息处理方法的全集,同时提供了各方法的默认实现。对应于中介者模式的参与者,InvChgColleegue是同事接口Colleegue。下面的代码给出了InvChgColleegue的声明。
上述代码中,26、36、44行声明了库存相关通知、方法。11行,声明了库存改变中介者成员变量invChgMediator,它通过有参构造方法初始化(17行),并将自身注册到中介者(18行)。
4.5 ProductInShoppingCart
ProductInShoppingCart是购物车中的商品信息,用于维护购物车中单个商品的名称、数量和库存状态。ProductInShoppingCart不是中介者模式的参与者。下面的代码给出了ProductInShoppingCart的声明。
4.6 ShoppingCart
ShoppingCart是购物车类,派生于库存改变同事类InvChgColleegue。ShoppingCart重写了其感兴趣的通知处理方法并声明了自己的业务方法。对应于中介者模式的参与者,ShoppingCart是具体同事类ConcreteColleegue。下面的代码给出了ShoppingCart的声明。
上述代码中,14行,声明了表征购物车中的产品集合的成员变量products。37行,重写库存改变事件处理方法inventoryChg,遍历购物车中的产品,改变其库存状态。56行,业务方法addProduct,库存充足时向购物车添加产品。69行,业务方法buyProduct,结账购买购物车中指定的产品。首先判断该产品的库存状态,仅库存充足时方能结账;其次,将该产品从购物车中移除(75行),并将产品被消费的事件通知中介者(76行)。86行,业务方法printDetail,打印购物车中的产品明细。
4.7 Warehouse
Warehouse是仓库类,派生于库存改变同事类InvChgColleegue。Warehouse重写了其感兴趣的通知处理方法并声明了自己的业务方法。对应于中介者模式的参与者,Warehouse是具体同事类ConcreteColleegue。下面的代码给出了Warehouse的声明。
上述代码中,14行,成员变量inventoryMap维护了各产品的库存信息。37行,重写获取指定产品的库存余量方法getTotal,返回当前仓库中的指定产品数量。49行,重写产品被消费的消息处理方法productConsumed,出库指定数量的对应产品,库存不足时全部出库,并返回实际出库数量。69行,业务方法remove,出库指定数量的产品,更新库存记录(72行)并将库存改变事件通知中介者(73行)。83行,业务方法add,入库指定数量的产品,更新库存记录(86行)并将库存改变事件通知中介者(87行)。92行,业务方法printDetail,打印仓库中各产品的库存明细。
4.8 测试代码
为了测试本文中的代码,我们可以编写如下测试代码。测试代码中我们添加了两个仓库和两个购物车,并通过中介者使其共同协作。请结合代码中的注释信息分析运行结果。
编译运行后,得到如下测试结果:
库存不足,添加购物车失败
初始状态:
仓库’A’明细:
婴儿手口巾 * 5;
————————————
仓库’B’明细:
婴儿手口巾 * 10;
————————————
购物车’1’明细:
婴儿手口巾 * 13 ,状态:正常;
————————————
购物车’2’明细:
婴儿手口巾 * 14 ,状态:正常;
————————————
购物车1结账后:
仓库’A’明细:
婴儿手口巾 * 0;
————————————
仓库’B’明细:
婴儿手口巾 * 2;
————————————
购物车’1’明细:
————————————
购物车’2’明细:
婴儿手口巾 * 14 ,状态:库存不足;
————————————
仓库A补货后:
仓库’A’明细:
婴儿手口巾 * 12;
————————————
仓库’B’明细:
婴儿手口巾 * 2;
————————————
购物车’1’明细:
————————————
购物车’2’明细:
婴儿手口巾 * 14 ,状态:正常;
————————————
1 目的
中介者(Mediator)模式将相关联的类对象(同事)间的交互集中于中介者对象,将各同事对象间交互的复杂度转嫁于中介者。通常来说,交互对象与中介者间的关系可以形象的描述为消息接收与处理。从应用场景来说,中介者对接收到的消息有两种处理方式。第一种是转发消息,即中介者将收到的消息向所有与其相关的同事广播,此时中介者只起到消息转发和信息汇总的作用;第二种是处理消息,即中介者将消息选择性的发送给符合条件的同事,直接参与到业务逻辑处理。这种情况下,代码复杂度将进一步向中介者集中,中介者需要知道同事的具体类型,并了解各具体同事类的业务逻辑。本文中给出的示例采用第一种方式。
2 基本形态
中介者的基本形态如类图2-1所示。图2-1 中介者类图
3 参与者
结合图2-1,下面介绍各类在中介者设计模式中扮演的角色。3.1 Mediator
Mediator是中介者接口,声明了中介者向同事开放的各“事件通知”接口方法。Mediator内部维护了同事接口类型的对象集合,并提供了向中介者添加及移除同事的方法。
3.2 ConcreteMediator
ConcreteMediator是具体中介者,实现了Mediator接口。在实现“事件通知”接口方法时,ConcreteMediator根据业务逻辑调用相应同事对象的对应方法。
3.3 Colleegue
Colleegue是同事接口,内部维护了中介者接口Mediator的实例。另外,如果采用消息转发的形式使用中介者,那么Colleegue还声明了各消息处理接口方法。
3.4 ConcreteColleegue
ConcreteColleegue1和ConcreteColleegue2是具体同事类,实现了同事接口Colleegue。如果采用消息转发的形式使用中介者,在实现同事接口时,具体同事类会忽略对“不感兴趣”消息的处理。
4 代码实践
下面我们用一个业务场景实例来进一步讲解中介者的使用。4.1 场景介绍
某电商平台后台拥有多个仓库,支持多用户在线将商品加入购物车及结账购买购物车中的商品。
以下各节将介绍该场景各类的具体实现及其在中介者设计模式中所对应的参与者角色。
4.2 InvChgMediator
InvChgMediator是库存改变中介者类。InvChgMediator声明了维护同事列表的相应方法及库存改变相关的事件通知接口。对应于中介者模式的参与者,InvChgMediator是中介者接口Mediator。下面的代码给出了InvChgMediator的声明。
package demo.designpattern.mediator; import java.util.ArrayList; import java.util.List; /** * 库存改变中介者接口 * Created by LiMingzi on 2017/8/9. */ public abstract class InvChgMediator { /** * 库存改变同事集合 */ protected List<InvChgColleegue> invChgColleegues=new ArrayList<InvChgColleegue>(); /** * 获取指定产品的库存余量 * @param productName 产品名 * @return 产品库存余量 */ public abstract int getTotal(String productName); /** * 产品被消费 * @param productName 产品名 * @param count 消费数量 */ public abstract void productConsumed(String productName,int count); /** * 库存改变 * @param productName 产品名 */ public abstract void inventoryChg(String productName); /** * 添加同事 * @param invChgColleegue 要添加的同事 */ public void addColleegue(InvChgColleegue invChgColleegue){ invChgColleegues.add(invChgColleegue); } /** * 移除同事 * @param invChgColleegue 要移除的同事 */ public void removeColleegue(InvChgColleegue invChgColleegue){ invChgColleegues.remove(invChgColleegue); } }
上述代码中,14行,维护了库存改变同事集合invChgColleegues,41行和49行分别声明了添加同事方法addColleegue和移除同事方法removeColleegue。21行、28行和34行,声明了三个与产品库存相关的抽象方法。
4.3 InvChgMediatorImpl
InvChgMediatorImpl是库存改变中介者实现类,派生于库存改变中介者抽象类InvChgMediator。InvChgMediatorImpl实现了各库存相关方法。对应于中介者模式的参与者,InvChgMediatorImpl是具体中介者类ConcreteMediator。下面的代码给出了InvChgMediatorImpl的声明。
package demo.designpattern.mediator; /** * 库存变化中介者实现类 * Created by LiMingzi on 2017/8/10. */ public class InvChgMediatorImpl extends InvChgMediator{ /** * 获取指定产品的库存余量 * * @param productName 产品名 * @return 产品库存余量 */ @Override public int getTotal(String productName) { // 产品总数 int productCount=0; for (InvChgColleegue invChgColleegue : invChgColleegues) { productCount+=invChgColleegue.getTotal(productName); } return productCount; } /** * 产品被消费 * * @param productName 产品名 * @param count 消费数量 */ @Override public void productConsumed(String productName, int count) { if(getTotal(productName)>=count){ // 处理的商品数量 int handledProductCount = 0; for (InvChgColleegue invChgColleegue : invChgColleegues) { if(handledProductCount<count){ handledProductCount+=invChgColleegue.productConsumed(productName,count-handledProductCount); } } }else{ System.out.println("库存不足,操作失败"); } } /** * 库存改变 * * @param productName 产品名 */ @Override public void inventoryChg(String productName) { for (InvChgColleegue invChgColleegue : invChgColleegues) { invChgColleegue.inventoryChg(productName); } } }
上述代码中,15行,获取指定产品的库存余量方法getTotal,通过遍历同事集合,累加各同事持有的产品数量得到产品余量。31行,产品被消费方法productConsumed,在库存充足的情况下,遍历同事集合,执行出库操作,直至出库数量达到消费数量。51行,库存改变消息处理方法inventoryChg,遍历同事集合,向各同事广播该消息。
4.4 InvChgColleegue
InvChgColleegue是库存改变同事类。InvChgColleegue是抽象类,声明了库存相关的方法并提供了默认实现。因本例中采用的是消息转发的形式,故InvChgColleegue声明的是库存相关消息处理方法的全集,同时提供了各方法的默认实现。对应于中介者模式的参与者,InvChgColleegue是同事接口Colleegue。下面的代码给出了InvChgColleegue的声明。
package demo.designpattern.mediator; /** * 库存改变同事类 * Created by LiMingzi on 2017/8/9. */ public abstract class InvChgColleegue { /** * 库存改变中介者 */ protected InvChgMediator invChgMediator; /** * 库存改变中介者 * @param invChgMediator */ public InvChgColleegue(InvChgMediator invChgMediator) { this.invChgMediator=invChgMediator; invChgMediator.addColleegue(this); } /** * 获取指定产品的库存余量 * @param productName 产品名 * @return 产品库存余量 */ public int getTotal(String productName){ return 0; } /** * 产品被消费 * @param productName 产品名 * @param count 消费数量 * @return 实际处理数量 */ public int productConsumed(String productName,int count){ return 0; } /** * 库存改变 * @param productName 产品名 */ public void inventoryChg(String productName){ } }
上述代码中,26、36、44行声明了库存相关通知、方法。11行,声明了库存改变中介者成员变量invChgMediator,它通过有参构造方法初始化(17行),并将自身注册到中介者(18行)。
4.5 ProductInShoppingCart
ProductInShoppingCart是购物车中的商品信息,用于维护购物车中单个商品的名称、数量和库存状态。ProductInShoppingCart不是中介者模式的参与者。下面的代码给出了ProductInShoppingCart的声明。
package demo.designpattern.mediator; /** * 购物车中的商品信息 * Created by LiMingzi on 2017/8/9. */ public class ProductInShoppingCart { /** * 商品名 */ private String productName; /** * 数量 */ private int count; /** * 状态,1为正常,0为库存不足 */ private int state; /** * 构造方法 * @param productName 产品名 * @param count 产品数量 */ public ProductInShoppingCart(String productName, int count) { this.productName = productName; this.count = count; this.state = 1; } /** * 获取产品名 * @return 产品名 */ public String getProductName() { return productName; } /** * 获取数量 * @return 产品数量 */ public int getCount() { return count; } /** * 获取状态 * @return 状态 */ public int getState() { return state; } /** * 设置库存状态 * @param state 库存状态 */ public void setState(int state) { this.state = state; } @Override public String toString() { return productName+" * "+count+" ,状态:"+(state==1?"正常":"库存不足")+";"; } }
4.6 ShoppingCart
ShoppingCart是购物车类,派生于库存改变同事类InvChgColleegue。ShoppingCart重写了其感兴趣的通知处理方法并声明了自己的业务方法。对应于中介者模式的参与者,ShoppingCart是具体同事类ConcreteColleegue。下面的代码给出了ShoppingCart的声明。
package demo.designpattern.mediator; import java.util.ArrayList; import java.util.List; /** * 购物车 * Created by LiMingzi on 2017/8/9. */ public class ShoppingCart extends InvChgColleegue { /** * 产品集合 */ private List<ProductInShoppingCart> products = new ArrayList<ProductInShoppingCart>(); /** * 购物车名称 */ private String name; /** * 库存改变中介者 * * @param name 购物车名称 * @param invChgMediator 库存变化中介者 */ public ShoppingCart(String name,InvChgMediator invChgMediator) { super(invChgMediator); this.name=name; } /** * 库存改变 * * @param productName 产品名 */ @Override public void inventoryChg(String productName) { for (ProductInShoppingCart product : products) { if (product.getProductName().equals(productName)) { if (product.getState() == 0 && invChgMediator.getTotal(productName) >= product.getCount()) { product.setState(1); }else if(product.getState() == 1 && invChgMediator.getTotal(productName) < product.getCount()){ product.setState(0); } break; } } } /** * 添加产品 * * @param name 产品名 * @param count 数量 */ public void addProduct(String name, int count) { if (invChgMediator.getTotal(name) >= count) { products.add(new ProductInShoppingCart(name, count)); } else { System.out.println("库存不足,添加购物车失败"); } } /** * 购买购物车中的产品 * * @param name 产品名 */ public void buyProduct(String name) { for (ProductInShoppingCart product : products) { if (product.getProductName().equals(name)) { if(product.getState()==0){ System.out.println("库存不足,链接失效"); }else { products.remove(product); invChgMediator.productConsumed(product.getProductName(), product.getCount()); } break; } } } /** * 打印明细 */ public void printDetail() { System.out.println("购物车'"+name+"'明细:"); for (ProductInShoppingCart product : products) { System.out.println(product.toString()); } System.out.println("------------------------------------"); } }
上述代码中,14行,声明了表征购物车中的产品集合的成员变量products。37行,重写库存改变事件处理方法inventoryChg,遍历购物车中的产品,改变其库存状态。56行,业务方法addProduct,库存充足时向购物车添加产品。69行,业务方法buyProduct,结账购买购物车中指定的产品。首先判断该产品的库存状态,仅库存充足时方能结账;其次,将该产品从购物车中移除(75行),并将产品被消费的事件通知中介者(76行)。86行,业务方法printDetail,打印购物车中的产品明细。
4.7 Warehouse
Warehouse是仓库类,派生于库存改变同事类InvChgColleegue。Warehouse重写了其感兴趣的通知处理方法并声明了自己的业务方法。对应于中介者模式的参与者,Warehouse是具体同事类ConcreteColleegue。下面的代码给出了Warehouse的声明。
package demo.designpattern.mediator; import java.util.HashMap; import java.util.Map; /** * 仓库类 * Created by LiMingzi on 2017/8/10. */ public class Warehouse extends InvChgColleegue { /** * 库存字典 */ private Map<String,Integer> inventoryMap= new HashMap<String, Integer>(); /** * 仓库名 */ private String name; /** * 构造方法 * * @param name 仓库名 * @param invChgMediator 库存改变中介者 */ public Warehouse(String name,InvChgMediator invChgMediator) { super(invChgMediator); this.name=name; } /** * 获取指定产品的库存余量 * * @param productName 产品名 * @return 产品库存余量 */ @Override public int getTotal(String productName) { return inventoryMap.containsKey(productName)?inventoryMap.get(productName):0; } /** * 产品被消费 * * @param productName 产品名 * @param count 消费数量 * @return 处理的产品数量 */ @Override public int productConsumed(String productName, int count) { // 处理的商品数量 int handledProductCount = 0; // 库存中存在商品 if(inventoryMap.containsKey(productName)){ if(inventoryMap.get(productName)<count){ handledProductCount=inventoryMap.get(productName); }else{ handledProductCount=count; } remove(productName,handledProductCount); } return handledProductCount; } /** * 出库 * @param productName 产品名 * @param count 出库数量 */ public void remove(String productName,int count){ // 库存充足 if(inventoryMap.containsKey(productName)&&inventoryMap.get(productName)>=count){ inventoryMap.put(productName,inventoryMap.get(productName)-count); invChgMediator.inventoryChg(productName); }else{ System.out.println("库存不足,出库失败"); } } /** * 入库 * @param productName 产品名 * @param count 入库数量 */ public void add(String productName,int count){ // 原库存 int originalCount = inventoryMap.containsKey(productName)?inventoryMap.get(productName):0; inventoryMap.put(productName,originalCount+count); invChgMediator.inventoryChg(productName); } /** * 打印明细 */ public void printDetail() { System.out.println("仓库'"+name+"'明细:"); for (Map.Entry<String, Integer> inventoryEntry : inventoryMap.entrySet()) { System.out.println(inventoryEntry.getKey()+" * "+inventoryEntry.getValue()+";"); } System.out.println("------------------------------------"); } }
上述代码中,14行,成员变量inventoryMap维护了各产品的库存信息。37行,重写获取指定产品的库存余量方法getTotal,返回当前仓库中的指定产品数量。49行,重写产品被消费的消息处理方法productConsumed,出库指定数量的对应产品,库存不足时全部出库,并返回实际出库数量。69行,业务方法remove,出库指定数量的产品,更新库存记录(72行)并将库存改变事件通知中介者(73行)。83行,业务方法add,入库指定数量的产品,更新库存记录(86行)并将库存改变事件通知中介者(87行)。92行,业务方法printDetail,打印仓库中各产品的库存明细。
4.8 测试代码
为了测试本文中的代码,我们可以编写如下测试代码。测试代码中我们添加了两个仓库和两个购物车,并通过中介者使其共同协作。请结合代码中的注释信息分析运行结果。
/** * 中介者测试 */ public static void mediatorTest(){ // 库存改变中介者 InvChgMediator invChgMediator = new InvChgMediatorImpl(); // 仓库A Warehouse warehouseA=new Warehouse("A",invChgMediator); // 仓库B Warehouse warehouseB=new Warehouse("B",invChgMediator); // 购物车1 ShoppingCart shoppingCart1=new ShoppingCart("1",invChgMediator); // 购物车2 ShoppingCart shoppingCart2=new ShoppingCart("2",invChgMediator); warehouseA.add("婴儿手口巾",5); warehouseB.add("婴儿手口巾",10); // 库存不足添加将失败 shoppingCart1.addProduct("婴儿手口巾",20); // 库存充足添加将成功 shoppingCart1.addProduct("婴儿手口巾",13); shoppingCart2.addProduct("婴儿手口巾",14); System.out.println("初始状态:"); warehouseA.printDetail(); warehouseB.printDetail(); shoppingCart1.printDetail(); shoppingCart2.printDetail(); shoppingCart1.buyProduct("婴儿手口巾"); System.out.println("购物车1结账后:"); warehouseA.printDetail(); warehouseB.printDetail(); shoppingCart1.printDetail(); shoppingCart2.printDetail(); shoppingCart1.buyProduct("婴儿手口巾"); warehouseA.add("婴儿手口巾",12); System.out.println("仓库A补货后:"); warehouseA.printDetail(); warehouseB.printDetail(); shoppingCart1.printDetail(); shoppingCart2.printDetail(); }
编译运行后,得到如下测试结果:
库存不足,添加购物车失败
初始状态:
仓库’A’明细:
婴儿手口巾 * 5;
————————————
仓库’B’明细:
婴儿手口巾 * 10;
————————————
购物车’1’明细:
婴儿手口巾 * 13 ,状态:正常;
————————————
购物车’2’明细:
婴儿手口巾 * 14 ,状态:正常;
————————————
购物车1结账后:
仓库’A’明细:
婴儿手口巾 * 0;
————————————
仓库’B’明细:
婴儿手口巾 * 2;
————————————
购物车’1’明细:
————————————
购物车’2’明细:
婴儿手口巾 * 14 ,状态:库存不足;
————————————
仓库A补货后:
仓库’A’明细:
婴儿手口巾 * 12;
————————————
仓库’B’明细:
婴儿手口巾 * 2;
————————————
购物车’1’明细:
————————————
购物车’2’明细:
婴儿手口巾 * 14 ,状态:正常;
————————————
相关文章推荐
- 设计模式讲解与代码实践(十一)——外观
- 设计模式讲解与代码实践(十九)——备忘录
- 设计模式讲解与代码实践(零)——序
- 设计模式讲解与代码实践(十四)——职责链
- 设计模式讲解与代码实践(九)——组合
- 设计模式讲解与代码实践(四)——原型
- 设计模式讲解与代码实践(七)——适配器(基于对象)
- 设计模式讲解与代码实践(一)——抽象工厂
- 设计模式讲解与代码实践(二十二)——策略
- 设计模式讲解与代码实践(五)——单例
- 设计模式讲解与代码实践(二十一)——状态
- 设计模式讲解与代码实践(二十四)——访问者
- 设计模式讲解与代码实践(二)——生成器
- 设计模式讲解与代码实践(二十)——观察者
- 设计模式讲解与代码实践(六)——适配器(基于类)
- 设计模式讲解与代码实践(二十三)——模板方法
- 设计模式讲解与代码实践(十五)——命令
- 设计模式讲解与代码实践(八)——桥接
- 设计模式讲解与代码实践(三)——工厂方法
- 设计模式讲解与代码实践(十六)——解释器