前言策略模式 —— 模板模式的改进
解决改进 状态模式
switch 常规写法改进实际应用类泛滥状态转换关系复杂的问题 后记
前言你是否被一群if else 所烦恼 可能前边写代码一时爽 后边添加逻辑更改需求火葬场?
有时候要处理分类的问题 你的代码就是一群switch 和case 稍微一不小心改错了 全部GG 随着需求增加 每个case变得臃肿不堪 那个时候有没有想过有些优雅的方式来解决case泛滥这个问题呢?
很多时候 状态 策略都具有一些类似的 共性的特征 但是代码上还是不停的重复 你很想改变现状 想让代码简洁 但是无从下手。。
我们可以优雅的封装各个策略 这样策略形成了策略树(比如父策略 子策略) 同样我们可以封装状态 形成状态树 可以自顶而下 先规则 再具体变化 先提取共性再管特性 这些过去我们所学的方法 都是可行的
策略模式 —— 模板模式的改进封装变化的设计原则 找到变化的部分 和不变的部分分开
之前是把回家的人做分类 现在需求增加 需要更细致的分辨度——回家方式
然后实现回家方式的自我组合
策略模式的核心是 对每种策略进行封装 使之相互交换 Travelable 策略接口 By air ByCoach ByTrain 不同的各种策略实现
而最终 PassengerByAirAndCoach 都是不同策略组合的产物
public class PassengerByAirByCoach extends People { private Travelable first; private Travelable second; public PassengerByAirAndCoach() { first = new ByAir(); second = new ByCoach(); } @Override protected void travel(){ first.travel(); second.travel(); }改进
虽然避免了代码的重复 我们只是引用具体的策略实现 但是没有重复这些类似组件的策略
可是每种策略组合的产物 都需要创造新的类 来 实际上类还是泛滥的
其实 使用这些策略组件 排列组合 不一定只是写死的 如下:
public class People { private ListtransportationList; public People() { transportationList = new linkedList (); } public void travel() { for (Travellable travel : transportationList) travel.travel(); } }
这里有个原则 想要使用别人的属性功能 优先使用 组合 而不是继承
Favor Composition over Inheritance
子类父类耦合度太高
不能够实现特性的混合
父类的私有性质会给子类带来麻烦
而组合 都是利用公有的性质!
运行的时候可以动态改变!!! 比如这里我们可以改链表
但是 如果继承 那就是写死的!
学过数电的兄弟应该知道状态机这个东西 这玩意可不是那些还给老师的知识 在实际生产实践中用的也不少 比方说我们要描述用户账户的几种状态 正常(N = normal) 警告(Warning) 封禁(F = Forbiddened) 三种状态 很明显这三种状态的转换关系是循环的 而且一级一级来循环:
那么就有三种状态和两种 *** 作(升级 降级 比如正常 升级为警告)
传统的方式恐怕是这么做的:
public void upGrade() { switch (state) { case Normal: state = Warning; break; case Warning: state = Forbid; break; case Forbid: state = Normal; break; } }
上边降级的代码我就不写了 一个道理
这种方法非常经典 但是有个问题 如果我新增了状态该怎么办 另外就是 目前这里只是单单状态而已 状态的转换过程也并不复杂 那也没啥好像
但如果是复杂的场合 可能这个case里边就几百行代码了 似乎也不方便吧
综上 传统方式不符合OCP开闭原则 维护起来困难
改进之前的策略模式是把策略行为进行封装 省得一大堆if else 这次呢?我们同样可以把状态封装 省下一堆的switch case
public interface State { void upGrade(User user); void downGrade(User user); String name(); } // state 自己维护 状态转移的关系 而不是在公共的switch里边! public class Normal implements State { public void upGrade(User user){ user.setState(new Warning()); } public String name(){ return "normal" } } // warning forbid 省略了 @Data // lombok 提供所有属性的getter setter public class User{ public String name; public String pwd; public State state = new Normal(); public void upGrade(){ this.state.upGrade(this); // 注意这里是 让自己的state属性 切换状态! } }
可以发现 我们要是想要添加新状态 很简单 再创一个类就完事了 而不像原来 要在那个switch 那个维护了所有状态转换的代码块里边去找 去修改 那多不优雅 这就符合了OCP开闭原则的设计理念
其实这里也有好莱坞模式 或者说观察者模式 的影子 发现了没 不是我user去找你所有的状态 去维护你们这么多状态之间的转换 ! 而是 你这些状态给我提供状态装换的接口 你们自己想办法状态转换 我只是打个信号
这也符合单一职责的原则的标准 同样也有“迪米特原则 最少知识原则的影子 就是我User不需要了解你们State的状态切换的细节 我不管 我只打信号 我的 *** 作 是基于我所知道的 就是我需要的 *** 作——升级降级
实际应用这里简单提一提 比方说 我们状态改变会涉及到权限的变化 我们可以用一个数据结构来记录权限 最简单的 所有的权限可以设计为一个列表 然后true代表allow允许 false代表禁止forbid
这样每次需要鉴权的时候 直接访问User对象本身 然后访问其state属性 然后查state里边的权限表就ok了
需要更改权限的时候 找那个state改一下就完事了
当然权限表可以设计的有层次 可以用一个接口规范+多个实现类 组成权限对象 多层次的管理复杂的权限环境
而且 这个state里边记录权限的数据结构还可以是引用 这样的话 权限可以集中管理 state只是引用罢了
这边考虑到 权限的改动不是很频繁 所以没打算给权限对象 设计开闭原则
类泛滥如果觉得这个会导致类的泛滥(说实话很难 因为状态实际生产环境中也没多少) 可以使用枚举类
这样 代码就只有一个枚举类了
不过枚举类的实现 还是沿袭了Effective java提到的 TypeSafe Enum模式 意味着 编译的时候会生成一个继承自Enum 实现了State接口的类 然后各个状态使用数组来维护 仅此而已
之后初始化的时候使用static静态代码块
怎么说呢 我觉得这反而导致了 所有状态类都挤在一个Enum里边 感觉并不是很划算
真要出现状态很多的情况 那就还得沿用策略模式的方式 进一步划分粒度 一般复杂的状态都是基本状态排列组合的结果 所以我们需要做的就是类似 策略模式 重新搞出最小的基本状态 然后支持排列组合(比如使用数据结构来记录顺序) 这样就能应对状态类的泛滥问题
状态转换关系复杂的问题虽然不常见 但假设发生
那我们直接把状态节点作为图论节点 这样状态转换就变成有向图的问题
之后 我们直接使用存储图的数据结构比如邻接矩阵 数组链表(看具体情况 你的指标是什么 访问快还是省空间) 然后每次需要的时候查表完事了!这样就不用再每个状态类里边维护非常复杂的状态转换关系 不过状态转换关系也集中在了一个数据结构里边 或许也会有弊端
后记看 其实设计模式正如我序章说的 就是融会贯通的 他不能向背单词一样 一个个的去死记硬背最后也不知道怎么用 而是融会贯通!一个好的代码 那些原则基本都是符合的 如果不符合 也可能是为了节约成本 或者预估违反原则的损失不大 所以浪一会罢了
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)