您的位置:首页 > 其它

从问题角度来思考设计模式(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
更多技术干货:风匀坊
关注公众号:风匀坊
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: