策略模式是一种GoF设计模式,允许软件在运行时从一系列算法中选择一种。每种算法都在自己的类中实现,这使得它们的客户端可以互换。使用策略设计模式,类可以以不同的方式和不同的实现来执行相同的方法。这是Gamma等人在“设计模式”一书中的模式之一。
例如,考虑需要通勤的工人。他可以使用不同的策略,例如乘汽车,摩托车,自行车或地铁去。班级worker有一个领域transport。并且transport有方法commute。与算法的类Car,Motorcycle,Bicycle,和Metro。他们需要实现Transport接口。
该方法的调用worker.transport.commute可以使用不同的算法,即不同的策略。注意,上下文不负责选择合适的算法。这是由选择一种策略的客户端完成的。上下文只是从一种策略中执行该方法。因此,上下文独立于实现。
在本文中,我们将使用Java枚举实现策略设计模式。枚举具有比大多数程序员所知道的更多的功能。
一旦这种方法稍微违反了开放-封闭原则,就会对此提出批评。使用枚举不能在运行时添加新的实现。您应该更新代码。让我们看一下代码。
验证器策略模式
第一个示例是XML,JSON,YAML或CSV内容的文件格式验证器。在传统的Strategy实施中,您需要多个类,每种算法一个。使用枚举,您可以在每个常量中实施业务规则。
public enum ValidatorStrategy {
XML { @Override void doValidation(String content) { // The validation code goes here System.out.println("This is a XML content"); } }, JSON { @Override void doValidation(String content) { // The validation code goes here System.out.println("This is a JSON content"); } }, YAML { @Override void doValidation(String content) { // The validation code goes here System.out.println("This is a YAML content"); } }, CSV { @Override void doValidation(String content) { // The validation code goes here System.out.println("This is a CSV content"); } };
abstract void doValidation(String content);
}
|
上下文类从客户端接收策略,并在所选策略中执行算法。
public class ValidatorContext {
private ValidatorStrategy strategy;
public ValidatorContext(ValidatorStrategy strategy) { this.strategy = strategy; }
public void setValidator(ValidatorStrategy validator) { this.strategy = validator; }
public void runValidation(String content) { strategy.doValidation(content); }
}
|
最后一个组件是将验证策略传递到上下文的客户端。看到您validator.runValidation在设置策略后总是调用方法。
ValidatorContext validator = new ValidatorContext(ValidatorStrategy.XML); validator.runValidation("XML content");
validator.setValidator(ValidatorStrategy.YAML); validator.runValidation("YAML content");
validator.setValidator(ValidatorStrategy.JSON); validator.runValidation("JSON content");
|
日期格式化策略模式
这是策略设计模式的另一个示例。现在,我们有了一个更复杂的情况,上下文类会将实现传递给策略类。这样,可以使用像Spring这样的依赖注入引擎。
public enum DateFormatterStrategy { BRAZIL { @Override public String doFormat(DateFormatterImpl formatter, Date date) { return formatter.formatBrazil(date); } }, BELGIUM { @Override public String doFormat(DateFormatterImpl formatter, Date date) { return formatter.formatBelgium(date); } }, US { @Override public String doFormat(DateFormatterImpl formatter, Date date) { return formatter.formatUS(date); } };
public abstract String doFormat(DateFormatterImpl formatter, Date date);
}
|
实现类如下所示。它包含用于格式化每个国家/地区的日期的业务规则。public class DateFormatterImpl {
private SimpleDateFormat sdfBrasil = new SimpleDateFormat("dd/MM/yyyy"); private SimpleDateFormat sdfBelgium = new SimpleDateFormat("dd.MM.yyyy"); private SimpleDateFormat sdfUS = new SimpleDateFormat("MM/dd/yyyy");
public String formatBrazil(Date date) { return this.sdfBrasil.format(date); }
public String formatBelgium(Date date) { return this.sdfBelgium.format(date); }
public String formatUS(Date date) { return this.sdfUS.format(date); }
}
|
上下文类与之前看到的完全相同。我的意思是,它没有任何明显的区别。
public class DateFormatterContext {
private DateFormatterStrategy strategy; private DateFormatterImpl formatter = new DateFormatterImpl();
public DateFormatterContext(DateFormatterStrategy strategy) { this.strategy = strategy; }
public void setStrategy(DateFormatterStrategy strategy) { this.strategy = strategy; }
public String format(Date date) { return this.strategy.doFormat(formatter, date); }
}
|
最后一个客户端类与上一个客户端类非常相似。设置策略后,执行方法formatter.format,然后所选策略将执行正确的算法。
Date date = new Date(); DateFormatterContext formatter = new DateFormatterContext(DateFormatterStrategy.BELGIUM); System.out.println("Belgium: " + formatter.format(date));
formatter.setStrategy(DateFormatterStrategy.BRAZIL); System.out.println("Brazil: " + formatter.format(date));
formatter.setStrategy(DateFormatterStrategy.US); System.out.println("US: " + formatter.format(date));
|
结论
Java Enum是一种方便的特殊类型,具有高级功能,可以帮助程序员满足不同的需求。在这种情况下,我们与他们一起实施了战略设计模式。这种方法具有优点,例如简单性和较少的类。但是也有弊端,最值得注意的是它稍微打破了开放-封闭原则。