从问题角度来思考设计模式(3) - 行为编
2017-10-13 22:35
218 查看
目录
生成编结构编
行为编
让数据和处理逻辑分离
改造前public class FooAmusementPark { private FooZoo zoo; private FooAquarium aquarium; public void enjoy(FooFamily family) { zoo.enjoy(); } public void enjoy(FooCouple couple) { aquarium.enjoy(); } }
当
Family,
Couple以外类型的用户增加时,FooAmusementPark类则必须扩充
改造后
Visitor
public class FooAmusementPark { private FooZoo zoo; private FooAquarium aquarium; public void accept(FooVisitor visitor) { visitor.visit(this); } } public class FooFamily extends FooVisitor { @Override public void visit(FooAmusementPark park) { park.getZoo().enjoy(); } } public class FooCouple extends FooVisitor { @Override public void visit(FooAmusementPark park) { park.getAquarium().enjoy(); } }
改造后,
Family,
Couple以外类型的用户增加时,
AmusementPark不需要更改,通过其中的
visit方法就能满足需求。
当数据结构不经常变化的,是使用
Visitor模式的考虑点。
需求多变时,如何分离处理逻辑
改造前public class FooController { private FooLatLngToPlaceAPI api; public String getFormattedAddress(double latitude, double longitude) { FooPlace place = api.getPlace(latitude, longitude); return place.getPostalCode() + System.lineSeparator() + place.getProvince() + " " + place.getCity(); } }
假设返回的数据
邮编 换行 省 市格式。下一次迭代可能又换成
邮编 省-市或者其它。当多个地方返回地址数据都希望保持同一的格式时,一个个去拼装字符串就显得有些繁琐了。
改造后
Strategy
public class FooController { private FooLatLngToPlaceAPI api; private FooAddressFormatter formatter = new FooAddressFormatter(); public String getFormattedAddress(double latitude, double longitude) { FooPlace place = api.getPlace(latitude, longitude); return formatter.format(place); } } public class FooAddressFormatter { public String format(FooPlace place) { return place.getPostalCode() + System.lineSeparator() + place.getProvince() + " " + place.getCity(); } }
将拼装返回数据的逻辑单独封装成一个类。
当需求变化时,也只需要改动这个类的逻辑即可,而又能保持全局格式统一。
当不想创建一个新类时,也可以这样子写
public class FooController { private FooLatLngToPlaceAPI api; private static final Function<FooPlace, String> FORMAT_ADDRESS = place -> place.getPostalCode() + System.lineSeparator() + place.getPrefecture() + " " + place.getCity(); public String getFormattedAddress(double latitude, double longitude) { FooPlace place = api.getPlace(latitude, longitude); return FORMAT_ADDRESS.apply(place); } }
根据条件区分近似的处理逻辑
改造前public String getFormattedText(String text, FormatType type) { switch (type) { case BOLD: return "**" + text + "**"; case ITALIC: return "*" + text + "*"; default: return text; } }
当
type值变多时,switch里的条件判断及代码块就变得越来越多了,不利代码维护。
改造后
Strategy + Factory
b8df
public class FooFormatterFactory { private FooFormatterFactory() {} public static FooFormatter create(FormatType type) { switch (type) { case BOLD: return new FooBoldFormatter(); case ITALIC: return new FooItalicFormatter(); default: return new FooFormatter() { @Override public String format(String text) { return text; } } } } } public class FooBoldFormatter extends FooFormatter { @Override public String format(String text) { return "**" + text + "**"; } } public class FooItalicFormatter extends FooFormatter { @Override public String format(String text) { return "*" + text + "*"; } }
public String getFormattedText(String text, FormatType type) { return FooFormatterFactory.create(type).format(text); }
改造后,当
type值变多时,
getFormattedText方法内容则不受影响。
通过
Strategy和
Factory模式结合使用的例子挺多的。
当不想创建新类的时,可以参考下面代码,用函数式接口实现
public class FooFormatterFactory { private FooFormatterFactory() {} public static UnaryOperator<String> create(FormatType type) { switch (type) { case BOLD: return text -> "**" + text + "**"; case ITALIC: return text -> "*" + text + "*"; default: return text -> text; } } }
public String getFormattedText(String text, FormatType type) { return FooFormatterFactory.create(type).apply(text); }
如何利用上一次处理的结果
改造前public class FooController { private int nextId; public FooResponse get(FooRequest request) { FooResponse response = getResponse(request); nextId = response.getNextId(); return response; } public FooResponse getNext() { FooRequest request = new FooRequest(nextId); return get(request); } private FooResponse getResponse(FooRequest request) { // 处理 return // 结果 } }
在
getNext()方法中把包含上一次的结果的nextId做为参数生成
request实例。当
request生成时所需要的参数的数量或类型改变时,
FooController中所有的值,逻辑等都需要变更。
改造后
Memento
public class FooMemento { private int nextId; public void update(FooResponse response) { this.nextId = response.getNextId(); } public FooRequest createNextRequest() { return new FooRequest(nextId); } }
public class FooController { private FooMemento memento = new FooMemento(); public FooResponse get(FooRequest request) { FooResponse response = getResponse(request); memento.update(response); return response; } public FooResponse getNext() { return get(memento.createNextRequest()); } private FooResponse getResponse(FooRequest request) { // 处理 return // 结果 } }
通过
Memento模式来记忆上次数据。
当新
request生成时所需要的参数变化时,只需要在
FooMemento中做修改。
Memento还经常用到单机游戏里,如小时候玩的存档型游戏,存档就是记录某进度下的角色们的各种状态,位置,关卡等信息。当读取存档时,其实也就是读取里面的状态数据,将程序恢复到该时刻。
查看原文:https://www.huuinn.com/archives/312
更多技术干货:风匀坊
关注公众号:风匀坊
相关文章推荐
- 从设计模式来说如何思考问题?
- 从设计模式来说如何思考问题?
- 知道大脑的思考模式吗? 问题对我们思想和行为影响
- 设计模式初探(四)——行为:问题为设计而生
- 设计模式初探(四)——行为:问题为设计而生
- 小议短网址系统的设计(有些时候,需要换个角度思考问题)
- 从追MM谈23种设计模式 --- 很经典! 学会用设计模式思考问题
- java23中设计模式——行为模式——Memento(备忘机制)
- 菜鸟学习 设计模式——行为模式(2)
- Java设计模式(21)——行为模式之备忘录模式(Memento)
- 设计模式-GOF行为模式(6-11)
- Java——单例设计模式中懒汉式并发访问的安全问题
- 浅谈Route组件的设计思考与模式
- 设计模式思考----单例模式
- 解决VS2008切换设计模式卡死问题
- 建造者模式:设计角度重温游戏中的角色
- 多线程三,同步函数、静态同步函数和单例设计模式中懒汉式即延迟加载模式的多线程问题(毕向东老师)
- 设计模式之行为模式-命令,迭代器,中介者,观察者,模板方法
- 牛客网Java刷题知识点之什么是单例模式?解决了什么问题?饿汉式单例(开发时常用)、懒汉式单例(面试时常用)、单例设计模式的内存图解
- C++技术问题总结-第12篇 设计模式原则