我有外观界面,用户可以在其中询问有关工程师的信息。该信息应作为我们为其创建DTO的JSON进行传输。现在请记住,我有多个数据源可以为该DTO列表提供一个项目。
因此,我现在相信我可以通过将数据源的处理程序添加到类型myEngineerListDTO
的{{1}}来使用装饰图案。因此,我的意思是所有数据源都具有相同的DTO。
下图显示了VerticalScrollbar和HorizontalScrollBar添加了不同的行为。这意味着它们会将行为添加到WindowDecorator界面。
我的问题是,我的情况适合装饰器模式吗?我是否特别需要添加行为以使用此模式?还有另一种适合我情况的模式吗?我已经考虑过责任链模式,但是由于我不需要在任何给定的时刻终止链条,因此我认为装饰模式可能会更好。
编辑:
我的最终结果应该是:来自所有数据源的List<EngineerDTO>
。我想要添加此模式的原因是,我可以轻松地在“管道”的其余部分后面添加另一个数据源。与其他数据源一样,该数据源将具有List<EngineersDTO>
方法。
答案 0 :(得分:1)
为了进一步说明如何Chain-of-responsibility pattern,我整理了一个小例子。我相信您应该能够适应这种解决方案,以适应您现实世界中的问题。
我们有一组未知的用户请求,其中包含要检索的属性的名称。有多个数据源,每个数据源具有不同数量的属性。我们要搜索所有可能的数据源,直到发现请求中的所有属性。某些数据类型和数据源可能看起来像下面的(请注意,为简便起见,我使用Lombok):
@lombok.Data
class FooBarData {
private final String foo;
private final String bar;
}
@lombok.Data
class FizzBuzzData {
private final String fizz;
private final String buzz;
}
class FooBarService {
public FooBarData invoke() {
System.out.println("This is an expensive FooBar call");
return new FooBarData("FOO", "BAR");
}
}
class FizzBuzzService {
public FizzBuzzData invoke() {
System.out.println("This is an expensive FizzBuzz call");
return new FizzBuzzData("FIZZ", "BUZZ");
}
}
我们的最终用户可能需要多种方式来解析数据。以下可能是有效的用户输入和预期的响应:
// Input
"foobar", "foo", "fizz"
// Output
{
"foobar" : {
"foo" : "FOO",
"bar" : "BAR"
},
"foo" : "FOO",
"fizz" : "FIZZ"
}
属性解析器的基本界面和简单的具体实现可能如下所示:
interface PropertyResolver {
Map<String, Object> resolve(List<String> properties);
}
class UnknownResolver implements PropertyResolver {
@Override
public Map<String, Object> resolve(List<String> properties) {
Map<String, Object> result = new HashMap<>();
for (String property : properties) {
result.put(property, "Unknown");
}
return result;
}
}
一个更好的解决方案可能是 “责任链模式” ,而不是使用常规的“装饰器模式” 。此模式与装饰器模式相似,但是,链中的每个链接都可以在项目上工作,忽略项目或结束执行。这有助于确定是否需要拨打电话,或者在完成请求工作后终止链。与装饰器模式的另一个区别是resolve
不会被每个具体类覆盖;我们的抽象类可以在需要时使用抽象方法调出子类。
回到当前的问题... 对于每个解析器,我们需要两个组件。一种从我们的远程服务中获取数据的方法,以及一种从检索到的数据中提取所有必需属性的方法。为了获取数据,我们可以提供一种抽象方法。为了从获取的数据中提取属性,我们可以制作一个小接口并维护这些提取器的列表,以查看可以从单个数据中提取多个属性的情况:
interface PropertyExtractor<Data> {
Object extract(Data data);
}
abstract class PropertyResolverChain<Data> implements PropertyResolver {
private final Map<String, PropertyExtractor<Data>> extractors = new HashMap<>();
private final PropertyResolver successor;
protected PropertyResolverChain(PropertyResolver successor) {
this.successor = successor;
}
protected abstract Data getData();
protected final void setBinding(String property, PropertyExtractor<Data> extractor) {
extractors.put(property, extractor);
}
@Override
public Map<String, Object> resolve(List<String> properties) {
...
}
}
resolve
方法的基本思想是首先评估此properties
实例可以实现哪个PropertyResolver
。如果存在符合条件的属性,则我们将使用getData
来获取数据。对于每个合格的属性,我们提取属性值并将其添加到结果图。每个无法解析的属性,都将请求successor
来解析该属性。如果解决了所有属性,执行链将结束。
@Override
public Map<String, Object> resolve(List<String> properties) {
Map<String, Object> result = new HashMap<>();
List<String> eligibleProperties = new ArrayList<>(properties);
eligibleProperties.retainAll(extractors.keySet());
if (!eligibleProperties.isEmpty()) {
Data data = getData();
for (String property : eligibleProperties) {
result.put(property, extractors.get(property).extract(data));
}
}
List<String> remainingProperties = new ArrayList<>(properties);
remainingProperties.removeAll(eligibleProperties);
if (!remainingProperties.isEmpty()) {
result.putAll(successor.resolve(remainingProperties));
}
return result;
}
当我们为PropertyResolverChain
实现具体的类时,我们将需要实现getData
方法并绑定PropertyExtractor
实例。这些绑定可以充当每个服务返回的数据的适配器。此数据可以采用与服务返回的数据相同的结构,也可以具有自定义架构。以前面的FooBarService
为例,我们的类可以像下面的一样实现(注意,我们可以有很多绑定,导致返回相同的数据)。
class FooBarResolver extends PropertyResolverChain<FooBarData> {
private final FooBarService remoteService;
FooBarResolver(PropertyResolver successor, FooBarService remoteService) {
super(successor);
this.remoteService = remoteService;
// return the whole object
setBinding("foobar", data -> data);
// accept different spellings
setBinding("foo", data -> data.getFoo());
setBinding("bar", data -> data.getBar());
setBinding("FOO", data -> data.getFoo());
setBinding("__bar", data -> data.getBar());
// create new properties all together!!
setBinding("barfoo", data -> data.getBar() + data.getFoo());
}
@Override
protected FooBarData getData() {
return remoteService.invoke();
}
}
将它们放在一起,我们可以调用Resolver
链,如下所示。我们可以观察到,仅当属性绑定到解析程序时,昂贵的getData
方法调用才每个Resolver
进行一次,并且用户仅获得他们所需的确切字段:
PropertyResolver resolver =
new FizzBuzzResolver(
new FooBarResolver(
new UnknownResolver(),
new FooBarService()),
new FizzBuzzService());
Map<String, Object> result = resolver.resolve(Arrays.asList(
"foobar", "foo", "__bar", "barfoo", "invalid", "fizz"));
ObjectMapper mapper = new ObjectMapper();
mapper.enable(SerializationFeature.INDENT_OUTPUT);
System.out.println(mapper
.writerWithDefaultPrettyPrinter()
.writeValueAsString(result));
输出
This is an expensive FizzBuzz call
This is an expensive FooBar call
{
"foobar" : {
"foo" : "FOO",
"bar" : "BAR"
},
"__bar" : "BAR",
"barfoo" : "BARFOO",
"foo" : "FOO",
"invalid" : "Unknown",
"fizz" : "FIZZ"
}