谈谈下订单的几种实现方式(用不同的模式实现:装饰器模式、代理模式、命令模式、状态模式、模版模式)
本文讲PlaceOrder函数的实现(重点在业务逻辑层),让我们来分别用不同的设计模式来实现吧:装饰器模式、代理模式、命令模式、状态模式、模版模式。
假设我们实现需求如下:
在PlaceOrder函数中需要做如下工作
1. 检查权限,未登录的不能递交订单
2. 计算税
3. 记录日志
好了,让我们分别来实现吧....当然,是用不同的设计模式分别实现。
装饰器模式实现
请看PlaceOrder函数方法体:
public bool PlaceOrder(OrderInfo order) { try { OrderService srv = new OrderService(); //核心业务类 TaxDecorator4OrderService tax = new TaxDecorator4OrderService(srv); //第一次装饰 LogDecorator4OrderService log = new LogDecorator4OrderService(tax); //第二次装饰 PermissionDecorator4OrderService permission = new PermissionDecorator4OrderService(log);//第三次装饰 return permission.NewOrder(order); //这里调用的是最后一个装饰器的方法(会链式反应,调用全部的关联函数) } catch(Exception ex)//由于PermissionDecorator4OrderService中会抛出exception,因此用了try, catch { Console.WriteLine("exception: "+ex.Message); return false; } }
实现方法
1. 写一个公共的接口,让“核心业务类”、“装饰类”都实现这个接口
2. “装饰类”的构造器需要能注入这个公共接口
3. 然后像上面那样拼装饰链条
装饰器代码如下(核心代码):
public class PermissionDecorator4OrderService:IOrderServiceComponent //上面所说的公共接口 { IOrderServiceComponent component; public PermissionDecorator4OrderService(IOrderServiceComponent component) //要提供注入的地方,此处是构造函数注入方式,你也可以根据情况使用其他注入方式 { this.component = component; } public bool NewOrder(OrderInfo order) { if (Identity.UserID <= 0) throw new Exception("Authorization exception"); return this.component.NewOrder(order); } }
代码在本页最下方有下载。
代理模式实现
请看PlaceOrder函数方法体:
public bool PlaceOrder(OrderInfo order) { try { OrderService srv = new OrderService(); //核心业务类(这个类就是要被控制访问的类) OrderServiceAgency srvAgency = new OrderServiceAgency(); //代理类,外界的调用是通过它的,由它来转发核心业务类的调用,意图在于控制访问 srvAgency.SetServiceComponent(srv); //此处为注入(方法注入方式),当然,你也可以使用构造器注入 return srvAgency.NewOrder(order); //看,此处调用的是代理的NewOrder } catch(Exception ex) { Console.WriteLine("exception: "+ex.Message); return false; } }
“核心业务类”、“代理类”,这2个类的外观是相同的,最简单的方式实现这个模式是
1. 为这2种类定义一个接口,这2种类都去实现
2. “代理类”除了要实现这个接口之外,还要持有这个接口的一个instance(这样才能转发调用)
3. “代理类”需要提供注入方式:构造函数、方法、属性注入方式
“代理类”代码如下(核心):
public class OrderServiceAgency : IOrderServiceComponent //实现同一个接口(外观一致) { private IOrderServiceComponent component; //要持有一个instance public void SetServiceComponent(IOrderServiceComponent component) //注入这个instance { this.component = component; } public bool NewOrder(OrderInfo order) //“代理类”的接口方法中,可以自定义一些逻辑 { if (Identity.UserID <= 0) throw new Exception("Authorization exception"); order.Total += order.Total * (decimal)0.02;//模拟税收 Console.WriteLine("loging..."); return this.component.NewOrder(order); //转发请求到“业务类” } }
代码在本页最下方有下载。
命令模式
请看PlaceOrder函数方法体:
public bool PlaceOrder(OrderInfo order) { try { NewOrderCommandResult result=new NewOrderCommandResult(); //由于命令模式不像直接call、返回结果方式,因此写了这个callback类,专门用来放执行结果 CheckPermissionCommand permissionCommand = new CheckPermissionCommand(new RealExecuters.PermissionService()); //检查权限命令 CalculateTaxCommand calculateTaxCommand = new CalculateTaxCommand(new RealExecuters.TaxCalculator(), order); //计算税命令 LogCommand logCommand = new LogCommand(new RealExecuters.LogService()); //记录日志命令 NewOrderCommand newOrderCommand=new NewOrderCommand(new RealExecuters.OrderService(), order, result); //调用"核心业务类"命令 List<IPlaceOrderCommand> list = new List<IPlaceOrderCommand>(); //把这些命令都打包到一个list中 list.Add(permissionCommand); list.Add(calculateTaxCommand); list.Add(logCommand); list.Add(newOrderCommand); list.ForEach(t=>t.Execute()); //遍历执行 return result.IsSuccess; //callback的执行结果 } catch(Exception ex) { Console.WriteLine("exception: "+ex.Message); return false; } }
原本的调用方式是直接call,然后目标对象返回结果,命令模式是在这步骤中间截取了一道,它通过增加一个command类来中转对目标方法的调用,此时,只要保存这些command类就能打包命令的执行了。
核心代码如下:
interface IPlaceOrderCommand //命令的抽象接口 { void Execute(); //就那么一个方法,Execute(), 而且是void和没有参数的 } class LogCommand : IPlaceOrderCommand { private LogService logService; public LogCommand(LogService logService) //不同的Command需要注入相应的真正实现这个命令的类 { this.logService = logService; } public void Execute() { this.logService.Log("loging..."); //只是中转调用 } }
class PermissionCheckCommand: IPlaceOrderCommand
class Log2Command:IPlaceOrderCommand
当这些XXXXXXXCommand被instance之后,就可以保存到Queue或者List,又或者序列化。。。。统一Execute(),而且此时执行的话,外观已经一致了,并且没有入参,很方便。
至于这个模式的callback result怎么写,大家就看看demo代码吧,这里不说了。
代码在本页最下方有下载。
状态模式
请看PlaceOrder函数方法体:
public bool PlaceOrder(OrderInfo order) { try { bool isValidaUser = Identity.UserID > 0; IOrderServiceComponent component=null; if (isValidaUser) //根据条件状态,去获取不同的对象,执行不一样的业务逻辑 component = new AuthorizaedNewOrderService(); else component = new UnAuthorizaedNewOrderService(); return component.NewOrder(order); } catch(Exception ex) { Console.WriteLine("exception: "+ex.Message); return false; } }
主要的原理是:把代码中大块的if/else中的代码extract到其他class中,实现要点:
1. 有几个分支,就写几个类,然后把if/else中的代码重构过去
2. 这些新增的类,需要实现同一个接口
核心代码如下:
public interface IOrderServiceComponent { bool NewOrder(OrderInfo order); } class UnAuthorizaedNewOrderService : IOrderServiceComponent { public bool NewOrder(OrderInfo order) { throw new Exception("Authorization exception"); } } class AuthorizaedNewOrderService : IOrderServiceComponent { public bool NewOrder(OrderInfo order) { order.Total += order.Total * (decimal)0.02;//模拟税收 //validate entity //insert database Console.WriteLine("inserting database"); Console.WriteLine("loging..."); return true; } }
代码在本页最下方有下载。
模版模式
请看PlaceOrder函数方法体:
public bool PlaceOrder(OrderInfo order) { try { BaseOrderService srv = new AaronOrderService(); //BaseOrderService是定义的模版抽象类,里面定义了NewOrder函数的主要步骤逻辑 return srv.NewOrder(order); //AaronOrderService只是重写/实现BaseOrderService的某些方法函数,达到部分自定义,全局固定的状态 } catch(Exception ex) { Console.WriteLine("exception: "+ex.Message); return false; } }
这个模式在平台设计上也很有用,因为能够做到全局固定、局部变化,简而言之:该不变的就不变、该变的就要变,也有称为热点的。
核心代码如下:
public abstract class BaseOrderService { public bool NewOrder(OrderInfo order) //这个就是骨架了,逻辑步骤是固定住的 { PermissionCheck(); TaxCalculate(order); bool success=CreateNewOrder(order); Log(); return success; } private void Log() { Console.WriteLine("loging..."); } protected abstract bool CreateNewOrder(OrderInfo order); //这个函数没有实现,是需要去实现的 protected virtual void TaxCalculate(OrderInfo order) //这个函数实现了,但是定义成了virtual, 允许子类override { order.Total += order.Total * (decimal)0.02; } private void PermissionCheck() { if (Identity.UserID <= 0) throw new Exception("Authorization exception"); } }
心怀远大理想。
为了家庭幸福而努力。