用户输入代码,该代码的类型由正则表达式确定。有许多不同类型的代码,例如EAN,ISBN,ISSN等。检测到类型后,必须为代码创建自定义查询。我认为为类型创建策略可能是一个好主意,但随着时间的推移它会感觉不对。
public interface SearchQueryStrategie {
SearchQuery createSearchQuery(String code);
}
-
public class IssnSearchQueryStrategie implements SearchQueryStrategie {
@Override
public SearchQuery createSearchQuery(final String code) {
// Create search query for issn number
}
}
-
public class IsbnSearchQueryStrategie implements SearchQueryStrategie {
@Override
public SearchQuery createSearchQuery(final String code) {
// Create search query for ISBN number
}
}
-
public class EanShortNumberSearchQueryStrategie implements SearchQueryStrategie {
@Override
public SearchQuery createSearchQuery(final String code) {
// Create search query for ean short number
}
}
-
public class TestApplication {
public static void main(final String... args) {
final String code = "1144875X";
SearchQueryStrategie searchQueryStrategie = null;
if (isIssn(code)) {
searchQueryStrategie = new IssnSearchQueryStrategie();
} else if (isIsbn(code)) {
searchQueryStrategie = new IsbnSearchQueryStrategie();
} else if (isEan(code)) {
searchQueryStrategie = new EanShortNumberSearchQueryStrategie();
}
if (searchQueryStrategie != null) {
performSearch(searchQueryStrategie.createSearchQuery(code));
}
}
private SearchResult performSearch(final SearchQuery searchQuery) {
// perform search
}
// ...
}
我不得不说还有更多策略。我该如何将代码发送到正确的策略? 我的第二种方法是在每个策略中放置一个布尔方法,以确定代码是否适合该策略。
public class TestApplication {
final SearchQueryStrategie[] searchQueryStrategies = {new IssnSearchQueryStrategie(), new IsbnSearchQueryStrategie(),
new EanShortNumberSearchQueryStrategie()};
public static void main(final String... args) {
final String code = "1144875X";
for (final SearchQueryStrategie searchQueryStrategie : searchQueryStrategie) {
if (searchQueryStrategie.isRightCode(code)) {
searchQueryStrategie.createSearchQuery(code);
break;
}
}
}
private SearchResult performSearch(final SearchQuery searchQuery) {
// perform search
}
// ...
}
您如何解决这个问题?战略模式是否适合我的目的?
答案 0 :(得分:2)
如果您使用的是Java 8,并且您可以从功能特性中获益,我认为一个Enum就足够了。
您可以通过使用将返回需要执行的查询的Function映射每种类型的代码来避免使用if / else语句:
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;
import java.util.regex.Pattern;
public enum CodeType
{
EAN("1|2|3"),
ISBN("4|5|6"),
ISSN("7|8|9");
String regex;
Pattern pattern;
CodeType(String regex)
{
this.regex = regex;
this.pattern = Pattern.compile(regex);
}
private static Map<CodeType, Function<String, String>> QUERIES =
new HashMap<>();
static
{
QUERIES.put(EAN, (String code) -> String.format("Select %s from EAN", code));
QUERIES.put(ISBN, (String code) -> String.format("Select %s from ISBB", code));
QUERIES.put(ISSN, (String code) -> String.format("Select %s from ISSN", code));
}
private static CodeType evalType(String code)
{
for(CodeType codeType : CodeType.values())
{
if (codeType.pattern.matcher(code).matches())
return codeType;
}
// TODO DON'T FORGET ABOUT THIS NULL HERE
return null;
}
public static String getSelect(String code)
{
Function<String, String> function = QUERIES.get(evalType(code));
return function.apply(code);
}
}
在主要内容中,您可以测试您的查询:
public class Main
{
public static void main(String... args)
{
System.out.println(CodeType.getSelect("1"));
// System.out: Select 1 from EAN
System.out.println(CodeType.getSelect("4"));
// System.out: Select 4 from ISBB
System.out.println(CodeType.getSelect("9"));
// System.out: Select 9 from ISSN
}
}
我通常倾向于保持代码尽可能紧凑。
有些人不喜欢枚举,所以我相信你可以使用普通的课程。
您可以进一步设计获得QUERIES(选择)的方式,因此您可以在那里拥有一个Runnable而不是String模板。
如果您不想使用Java 8的功能方面,可以使用与每种类型的代码相关联的Strategy对象:
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;
import java.util.regex.Pattern;
public enum CodeType2
{
EAN("1|2|3", new StrategyEAN()),
ISBN("4|5|6", new StrategyISBN()),
ISSN("7|8|9", new StrategyISSN());
String regex;
Pattern pattern;
Strategy strategy;
CodeType2(String regex, Strategy strategy)
{
this.regex = regex;
this.pattern = Pattern.compile(regex);
this.strategy = strategy;
}
private static CodeType2 evalType(String code)
{
for(CodeType2 codeType2 : CodeType2.values())
{
if (codeType2.pattern.matcher(code).matches())
return codeType2;
}
// TODO DON'T FORGET ABOUT THIS NULL HERE
return null;
}
public static void doQuery(String code)
{
evalType(code).strategy.doQuery(code);
}
}
interface Strategy { void doQuery(String code); }
class StrategyEAN implements Strategy {
@Override
public void doQuery(String code)
{
System.out.println("EAN-" + code);
}
}
class StrategyISBN implements Strategy
{
@Override
public void doQuery(String code)
{
System.out.println("ISBN-" + code);
}
}
class StrategyISSN implements Strategy
{
@Override
public void doQuery(String code)
{
System.out.println("ISSN-" + code);
}
}
主要方法如下:
public class Main
{
public static void main(String... args)
{
CodeType2.doQuery("1");
CodeType2.doQuery("4");
CodeType2.doQuery("9");
}
}
答案 1 :(得分:1)
所以,策略模式确实是正确的选择,但strategy
本身是不够的。您有几种选择:
使用简单的if / else或switch Factory
。它很丑陋,容易因新策略而扩展,但实施简单快捷。
使用注册表。在应用程序初始化阶段,您可以使用正确的代码在registry
每个SearchQueryStratgeyFactory中注册。例如,如果您使用简单的Map
,您可以这样做:
strategyRegistry.put("isbn", new IsbnSearchStrategyFactory());
strategyRegistry.put("ean", new EanSearchStrategyFactory());
.... and so on
然后,当您需要使用代码ID从地图中get()
策略工厂时,您需要获得正确的策略。如果您有很多策略,这种方法会更好,但在应用程序启动期间需要一个附加的初始化步骤。
使用服务定位器。 ServiceLocator是一种支持动态查找实现的模式。 Java附带了ServiceLocator模式的实现 - &gt;臭名昭着的ServiceLoader
班。这是我最喜欢的方法,因为它允许消费者和实现的完全解耦。使用服务定位器,您可以轻松添加新策略,而无需修改现有代码。我不会解释如何使用ServiceLoader - 网上有大量信息。我只想提一下,使用服务定位器,你需要实现“可以处理这样的代码吗?”每个战略工厂的逻辑。例如,如果工厂无法为“isbn”创建策略,则返回null并尝试使用下一个工厂。
另请注意,在所有情况下,您都与制作策略实施的工厂合作。
PS:strategy
而非strategie
:)
答案 2 :(得分:0)
我建议使用Factory
模式。它更好地描述和处理您的场景。
答案 3 :(得分:0)
您可以按以下方式设计(使用工厂DP和多态的概念):
Code
作为界面。
ISSNCode
,ISBNCode
和EANCode
作为具体类
实现Code
接口,让单arg构造函数将文本作为String。
Code
有方法getInstanceOfCodeType(String text)
,它返回Code
子类的实例(通过检查传递给它的text
的类型来决定)。我们假设返回的值为code
班级SearchQueryStrategieFactory
getSearchQueryStrategie(code)
方法。它使用步骤3中返回的值,并生成不同的值
使用SearchQueryStrategie
运算符基于code
类型的new
子类的实例,然后返回相同的内容。
因此,您需要从任何地方调用两个方法getInstanceOfCodeType(text)
和getSearchQueryStrategie(code)
。
代替 隐式 在main中实现工厂,将整个 factory 代码分开,使其易于维护和扩展。
答案 4 :(得分:0)
您的方法不是战略模式。策略模式是通过将备用Context
对象传递给它来自定义对象(Strategy
在此模式方面)的行为。通过这种方式,我们不需要修改Context
类的源代码,但仍然可以自定义从其实例化的对象的行为。
您的问题与您有一个请求(您的code
)的责任链(CoR)模式有些相关,需要弄清楚预定义列表中的哪个SearchQueryStrategie
应该处理请求。
你提到的第二种方法 - 使用数组 - 很好。但是,为了使它在生产代码中可用,您必须有另一个对象 - 比方说Manager
- 管理数组并负责查找每个请求的相关元素。因此,您的客户端代码必须依赖于两个对象:Manager
和结果SearchQueryStrategie
。正如您所看到的,Manager
类的源代码往往会经常更改,因为SearchQueryStrategie
的新实现可能会出现。这可能会让您的客户感到恼火。
这就是CoR Pattern使用链表机制而不是数组的原因。每个SearchQueryStrategie
对象A将保存对下一个SearchQueryStrategie
B的引用。如果A无法处理请求,它将委托给B(它甚至可以在委托之前修饰请求)。当然,某些地方仍然必须知道所有类型的策略并创建SearchQueryStrategie
的链接列表,但是您的客户端将仅依赖于SearchQueryStrategie
对象(列表中的头部)。
以下是代码示例:
class SearchQueryConsumer {
public void consume(SearchQuery sq) {
// ...
}
}
abstract class SearchQueryHandler {
protected SearchQueryHandler next = null;
public void setNext(SearchQueryHandler next) { this.next = next; }
public abstract void handle(String code, SearchQueryConsumer consumer);
}
class IssnSearchQueryHandler extends SearchQueryHandler {
@Override
public void handle(String code, SearchQueryConsumer consumer) {
if (issn(code)) {
consumer.consume(/* create a SearchQuery */);
} else if (next != null) {
next.handle(code, consumer);
}
}
private boolean issn(String code) { ... }
}