对设计模式的总结之简单工厂与策略模式
前言
面向对象编程追求的本质-提高扩展性、可维护性、灵活性和复用性。合理利用面向对象6个原则,能够很好的达到要求。如何利用好就是至关重要的了,前人总结了23+个设计模式能够让初学者更容易学到其中的精髓,本文就说说我对本人对简单工厂模式、策略模式的见解。
设计模式链接
简单工厂模式与策略模式
简单工厂模式
工作中,常常遇到需要做一个功能(鸭子),这个功能中含有可控个数的子操作功能(鸭子叫,鸭子跑,鸭子飞),而且子功能在不同的情况下处理方式又不相同(成年鸭子/小鸭子叫,成年鸭子/小鸭子跑,成年鸭子/小鸭子飞)。我们首先就会想到,用简单工厂模式哇。创建一个该功能的抽象基类,再创建多个实现不同逻辑的子类继承它。最后建立一个工厂类,通过工厂类中类型判断可生成不同的该功能子类实例。客户端最后调用工厂和抽象基类操作即可。实现类与类松耦合,既简单又实用。
功能需求:统一登录系统登录或登出不同的游戏。1、登录系统是统一的;2、固定两个功能-登录和登出;3、有限个游戏。
基本用法
-
//选取具体产品,返回实例 var gameOfInteractive = GameOfInteractive.GameService(1); //调用方法 gameOfInteractive.Login("sLoginName", "sPsw"); /// <summary> /// 工厂类 /// </summary> public class GameOfInteractive { /// <summary> /// 游戏返回基类 /// </summary> private static GameAbstract gameServiceCall = null; /// <summary> /// 工厂构造类 /// </summary> /// <param name="gameType"></param> public static GameAbstract GameService(int gameType) { try { switch (gameType) { case 1: gameServiceCall = new Sparrow(); break; case 2: gameServiceCall = new ShootBirds(); break; default: break; } } catch (Exception ex) { throw ex; } return gameServiceCall; } } /// <summary> /// 抽象基类 /// </summary> public abstract class GameAbstract { /// <summary> /// 游戏登录 /// </summary> /// <param name="sLoginName"></param> /// <param name="sPsw"></param> /// <returns></returns> public abstract string Login(string sLoginName, string sPsw); /// <summary> /// 游戏退出 /// </summary> /// <param name="sLoginName"></param> /// <param name="sPsw"></param> /// <returns></returns> public abstract string LoginOut(string sLoginName, string sPsw); } /// <summary> /// XX游戏具体类 /// </summary> public class ShootBirds: GameAbstract { /// <summary> /// 游戏登录 /// </summary> /// <param name="sLoginName"></param> /// <param name="sPsw"></param> /// <returns></returns> public override string Login(string sLoginName, string sPsw) { return "我通过HTTP请求和XX游戏服务交互进行登录"; } /// <summary> /// 游戏退出 /// </summary> /// <param name="sLoginName"></param> /// <param name="sPsw"></param> /// <returns></returns> public override string LoginOut(string sLoginName, string sPsw) { return "我通过HTTP请求和XX游戏服务交互,传输sLoginName和sPsw,通知客户下线"; } } /// <summary> /// YY游戏操作类 /// </summary> public class Sparrow : GameAbstract { /// <summary> /// 游戏登录 /// </summary> /// <param name="sLoginName"></param> /// <param name="sPsw"></param> /// <returns></returns> public override string Login(string sLoginName, string sPsw) { return "我通过socket和YY游戏服务交互进行登录"; } /// <summary> /// 游戏退出 /// </summary> /// <param name="sLoginName"></param> /// <param name="sPsw"></param> /// <returns></returns> public override string LoginOut(string sLoginName, string sPsw) { return "我通过socket和YY游戏服务交互,传输sLoginName和sPsw,通知客户下线"; } }
总结
优缺点:工厂模式初步满足了开放-封闭原则;工厂的使用,降低了对象之间的耦合性,做到了责任分离(客户端不直接创建对象实例,生产实例交给工厂来做)。
工厂与消费者、原料间的联系都很紧,如果工厂出了问题,与之相关的所有功能都将瘫痪;每次功能扩展,都需要改动工厂类,违反了高内聚责任分配原则。
使用场景:一般只在很简单的情况下应用,不关心构建,只关心使用。eg:多数据库选择,登录系统与其他系统做简单信息交互操作等。
策略模式
万物随时变,唯一不变的就是变化。实体经济萧条,商场生意不如以往,不想被拍死在沙滩上,就必须增加新的刺激消费的活动 。eg:满100返10,满500送自行车,会员卡送积分(积分可以换商品),多级会员打折等促销等方式。这些促销方式不是一次性就定下来的,是随着时间的改变产生的,如果在商场计价系统中直接用简单工厂模式,算法经常更改,工厂也随之更改,维护和扩展成本都会逐步增加。考虑到计价功能单一,每个子功能都只做计价,结构固定。可以把具体使用那种计价方式的任务交给客户端来控制,这样就产生了策略模式。策略模式与简单工厂模式很像,都有功能基类和各种扩展的子功能类,唯一不同的就是策略模式强调的是算法封装,不同的算法,用相应的子类进行实现。
功能需求:公司工资计算功能。1、只做工资计算,返回最终实得工资。2、有限个工种。3、后期可能还会添加新的工种。
基本用法
-
/// <summary> /// 环境角色 /// </summary> public class Context { /// <summary> /// 需要的策略 /// </summary> private AbstractStrategy strategy = null; /// <summary> /// 计算实际得到工资 /// </summary> /// <param name="overtime"></param> /// <returns></returns> public decimal CalculateSalary(int overtime) { return strategy.CalculateSalary(overtime); } } /// <summary> /// 抽象策略角色 /// </summary> public abstract class AbstractStrategy { protected static int baseOvertime = 80;//每个月基础加班时间 /// <summary> /// 计算实际得到工资 /// </summary> /// <param name="overtime">加班时间</param> /// <returns></returns> public abstract decimal CalculateSalary(int overtime); } /// <summary> /// 工人工资计算 /// </summary> public class WagesForWorkmen:AbstractStrategy { private static decimal baseSalary = 2800;//基础工资 private static decimal overtimeSalary = 37.5M;//每小时加班费 private static decimal extraSubsidies = 8.6M;//超出基础加班时间后每小时另加补助费用 /// <summary> /// 计算实际得到工资 /// </summary> /// <param name="overtime">加班时间</param> /// <returns></returns> public override decimal CalculateSalary(int overtime) { decimal actuallySalary = baseSalary; if (overtime> baseOvertime) { actuallySalary += overtimeSalary * baseOvertime + (overtime - baseOvertime) * (overtimeSalary + extraSubsidies); } else { actuallySalary += overtimeSalary * overtime; } return actuallySalary; } } /// <summary> /// 组长工资计算 /// </summary> public class WagesForGroupLeader:AbstractStrategy { private static decimal baseSalary = 2800;//基础工资 private static decimal postSalary = 800;//岗位工资 private static decimal overtimeSalary = 40M;//每小时加班费 private static decimal extraSubsidies = 5M;//超出基础加班时间后每小时另加补助费用 /// <summary> /// 计算实际得到工资 /// </summary> /// <param name="overtime">加班时间</param> /// <returns></returns> public override decimal CalculateSalary(int overtime) { decimal actuallySalary = baseSalary+ postSalary; if (overtime > baseOvertime) { actuallySalary += overtimeSalary * baseOvertime + (overtime - baseOvertime) * (overtimeSalary + extraSubsidies); } else { actuallySalary += overtimeSalary * overtime; } return actuallySalary; } } /// <summary> /// 经理工资计算 /// </summary> public class WagesForManager : AbstractStrategy { private static decimal baseSalary = 8000;//基础工资 private static decimal overtimeSalary =80M;//每小时加班费 private static decimal extraOvertimeSalary = 0M;//超出基础加班时间后每小时加班费 /// <summary> /// 计算实际得到工资 /// </summary> /// <param name="overtime">加班时间</param> /// <returns></returns> public override decimal CalculateSalary(int overtime) { decimal actuallySalary = baseSalary; if (overtime > baseOvertime) { actuallySalary += overtimeSalary * baseOvertime + (overtime - baseOvertime) * extraOvertimeSalary; } else { actuallySalary += overtimeSalary * overtime; } return actuallySalary; } }
总结
优缺点:相对于简单工厂模式,能避免工厂挂掉,其他相应功能都被牵连的问题;能满足客户端高频率更换功能实现算法要求(不用总是去改工厂类,只需要在客户端更改调用类)。
策略模式适用于客户端知道所有的算法或行为的情况,增加了具体功能与客户端的耦合度。
使用场景:商场计价功能、税收计算功能、保险行业的参保收益计价功能。
简单工厂与策略结合
使用简单工厂时,客户端需要知道工厂类和功能基类;基本的策略模式,将选择所用具体实现的职责交给客户端,本身没有减除客户端需要选择判断的压力。能否进优化?可以。将两者结合起来,让环境角色(StrategyContext)拥有选择策略和执行策略两种功能,客户端只需要传递类型参数调用环境角色即可。
基本用法
-
/// <summary> /// 环境角色 /// </summary> public class Context { /// <summary> /// 需要的策略 /// </summary> private AbstractStrategy strategy = null; /// <summary> /// 获取策略 /// </summary> /// <returns></returns> public Context(int iType) { switch (iType) { case 1: strategy = new WagesForWorkmen(); break; case 2: strategy = new WagesForGroupLeader(); break; case 3: strategy = new WagesForManager(); break; } } /// <summary> /// 计算实际得到工资 /// </summary> /// <param name="overtime"></param> /// <returns></returns> public decimal CalculateSalary(int overtime) { return strategy.CalculateSalary(overtime); } }
总结
优缺点:相对于简单工厂模式、单一策略模式,进一步实现了解耦(客户端只需和环境角色交互)。
虽然进一步解耦,但是任然存在简单工厂的问题。