gpt4 book ai didi

java - 使用装饰器模式而不添加 "different"行为

转载 作者:行者123 更新时间:2023-11-30 05:55:15 24 4
gpt4 key购买 nike

我有外观界面,用户可以在其中询问有关工程师的信息。该信息应以 JSON 形式传输,我们为其创建了 DTO。现在请记住,我有多个数据源可以向此 DTO 列表提供项目。

所以我相信现在我可以通过将数据源的处理程序添加到 myEngineerListDTO 来使用装饰模式类型 List<EngineerDTO> 。所以我的意思是所有数据源都有相同的 DTO。

下图显示了 VerticalScrollbar 和 Horizo​​ntalScrollBar 添加了不同的行为。这意味着他们向 WindowDecorator 接口(interface)添加行为。
enter image description here

我的问题,我的情况符合装饰器模式吗?我是否特别需要添加一个行为来使用此模式?还有其他模式适合我的情况吗?我已经考虑过责任链模式,但因为我不需要在任何给定时刻终止我的链,所以我认为装饰者模式可能会更好。

编辑:我的最终结果应该是:List<EngineersDTO>来自所有数据源。我想添加此模式的原因是这样我可以轻松地在“管道”的其余部分后面添加另一个数据源。该数据源与其他数据源一样,将具有 addEngineersDTOToList方法。

最佳答案

进一步说明如何 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)和简单的具体实现可能如下所示:

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)并维护这些提取器的列表,因为可以从单个数据中提取多个属性:

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 方法的基本思想是首先评估此 PropertyResolver 实例可以满足哪些属性。如果有符合条件的属性,那么我们将使用 getData 获取数据。对于每个符合条件的属性,我们提取属性值并将其添加到结果映射中。每个无法解析的属性,后继者将被请求解析该属性。如果所有属性都已解析,则执行链将结束。

@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 方法并绑定(bind) PropertyExtractor 实例。这些绑定(bind)可以充当每个服务返回的数据的适配器。该数据可以遵循与服务返回的数据相同的结构,或者具有自定义架构。使用前面的 FooBarService 为例,我们的类可以像下面这样实现(请注意,我们可以有许多绑定(bind),从而返回相同的数据)。。 p>

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 链,如下所示。我们可以观察到,仅当属性绑定(bind)到解析器时,昂贵的 getData 方法调用才对每个解析器执行一次,并且用户仅获取他们想要的确切字段。要求:

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"
}

关于java - 使用装饰器模式而不添加 "different"行为,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53300239/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com