从问题角度来思考设计模式(3)

来源:互联网 发布:淘宝客服怎么做兼职 编辑:程序博客网 时间:2024/05/01 06:49

目录

  1. 生成编
  2. 结构编
  3. 行为编

让数据和处理逻辑分离

  • 改造前
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
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方法内容则不受影响。通过StrategyFactory模式结合使用的例子挺多的。当不想创建新类的时,可以参考下面代码,用函数式接口实现
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
更多技术干货:风匀坊
关注公众号:风匀坊
原创粉丝点击