避免与策略模式耦合

发布于 2024-11-04 19:50:35 字数 1036 浏览 0 评论 0原文

我试图将策略模式应用于特定情况,但遇到了如何避免将每个具体策略耦合到为其提供数据的上下文对象的问题。以下是模式的简化情况,该模式以几种不同的方式发生,但应以类似的方式处理。

我们有一个对象Acquisition,它提供与特定时间帧相关的数据 - 基本上是使用不同硬件收集的一堆外部数据。由于它包含的数据量,它已经太大了,所以我不想再赋予它任何进一步的责任。我们现在需要获取一些数据,并根据某些配置向硬件发送相应的电压。

因此,想象一下以下(非常简化的)类:

class Acquisition
{
    public Int32 IntegrationTime { get; set; }
    public Double Battery { get; set; }
    public Double Signal { get; set; }
}

interface IAnalogOutputter
{
    double getVoltage(Acquisition acq);
}

class BatteryAnalogOutputter : IAnalogOutputter
{
    double getVoltage(Acquisition acq)
    {
        return acq.Battery;
    }
}

现在,每个具体策略类都必须与我的 Acquisition 类耦合,该类也是最有可能被修改的类之一,因为它是我们应用程序的核心。这仍然是对旧设计的改进,旧设计是 Acquisition 类中的一个巨大的 switch 语句。每种类型的数据可能有不同的转换方法(虽然电池是一个简单的传递,但其他的则根本没有那么简单),所以我觉得策略模式或类似的应该是要走的路。

我还要注意,在最终实现中,IAnalogOutputter 将是一个抽象类而不是接口。这些类将位于用户可配置的列表中并序列化为 XML 文件。该列表必须在运行时可编辑并被记住,因此可序列化必须是我们最终解决方案的一部分。万一它有所作为。

如何确保每个实现类都能获取其工作所需的数据,而不将其绑定到我最重要的类之一?或者我是否以完全错误的方式处理此类问题?

I am attempting to apply the Strategy pattern to a particular situation, but am having an issue with how to avoid coupling each concrete strategy to the context object providing data for it. The following is a simplified case of a pattern that occurs a few different ways, but should be handled in a similar way.

We have an object Acquisition that provides data relevant to a specific frame of time - basically a bunch of external data collected using different pieces of hardware. It's already too large because of the amount of data it contains, so I don't want to give it any further responsibility. We now need to take some of this data, and based on some configuration send a corresponding voltage to a piece of hardware.

So, imagine the following (much simplified) classes:

class Acquisition
{
    public Int32 IntegrationTime { get; set; }
    public Double Battery { get; set; }
    public Double Signal { get; set; }
}

interface IAnalogOutputter
{
    double getVoltage(Acquisition acq);
}

class BatteryAnalogOutputter : IAnalogOutputter
{
    double getVoltage(Acquisition acq)
    {
        return acq.Battery;
    }
}

Now, every concrete strategy class has to be coupled to my Acquisition class, which is also one of the most likely classes to be modified since it's core to our application. This is still an improvement over the old design, which was a giant switch statement inside the Acquisition class. Each type of data may have a different conversion method (while Battery is a simple pass-through, others are not at all that simple), so I feel Strategy pattern or similar should be the way to go.

I will also note that in the final implementation, IAnalogOutputter would be an abstract class instead of an interface. These classes will be in a list that is configurable by the user and serialized to an XML file. The list must be editable at runtime and remembered, so Serializable must be part of our final solution. In case it makes a difference.

How can I ensure each implementation class gets the data it needs to work, without tying it to one of my most important classes? Or am I approaching this sort of problem in the completely wrong manner?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

冬天旳寂寞 2024-11-11 19:50:35

策略模式封装了通常很复杂的操作/计算。

您想要返回的电压取决于

  • 配置
  • 采集数据的

一些 ,因此我会将它们放入另一个类中并将其传递给策略实现者。

同样在序列化方面,您没有序列化策略类,也许只有它们的名称或类型名称。


更新

好吧,看来您的实现只需要一份采集数据。这对于策略模式来说有点不寻常 - 但我不认为它更适合 Visitor 所以策略很好。我将创建一个类,除了实现者需要的配置之外,它还具有属性、获取数据(可能继承自它)。

Strategy Pattern encapsulates a - usually complex - operation/calculation.

The voltage you want to return is dependent on

  • pieces of configuration
  • Some of the acquisition data

So I would put these into another class and pass it to strategy implementors.

Also in terms of serialisation, you do not have serialise the strategy classes, perhaps only their name or type name.


UPDATE

Well, it seems that your implementations need only one piece of the acquisition data. That is a bit unusual for a strategy pattern - but I do not believe it fits Visitor better so strategy is fine. I would create a class which has as property, acquisition data (perhaps inherits from it) in addition to configuration that implementors need.

眼眸里的那抹悲凉 2024-11-11 19:50:35

您可以做的一件事是使用工厂方法来构建您的策略。您的各个策略只能在其构造函数中接受它们所需的各个数据元素,并且工厂方法是唯一需要知道如何在给定 Acquisition 对象的情况下填充该数据的方法。像这样的东西:

public class OutputterFactory
{
    public static IAnalogOutputter CreateBatteryAnalogOutputter(Acquisition acq)
    {
        return new BatteryANalogOutputter(acq.Battery);
    }



}

One thing you could do is use factory methods to construct your Strategies. Your individual strategies can take in their constructor only the individual data elements they need, and the factory method is the only thing that needs to know how to fill in that data given an Acquisition object. Something like this:

public class OutputterFactory
{
    public static IAnalogOutputter CreateBatteryAnalogOutputter(Acquisition acq)
    {
        return new BatteryANalogOutputter(acq.Battery);
    }



}

好吧,我不想在这里把功劳归功于别人,但我找到了一个非常适合我的目的的混合解决方案。它完美地序列化,并极大地简化了新输出类型的添加。关键是一个接口,IOutputValueProvider。另请注意,此模式如何轻松地处理不同存储数据方式的检索(例如字典而不是参数)。

interface IOutputValueProvider
{
    Double GetBattery();
    Double GetSignal();
    Int32 GetIntegrationTime();
    Double GetDictionaryValue(String key);
}

interface IAnalogOutputter
{
    double getVoltage(IOutputValueProvider provider);
}

class BatteryAnalogOutputter : IAnalogOutputter
{
    double getVoltage(IOutputValueProvider provider)
    {
        return provider.GetBattery();
    }
}

class DictionaryValueOutputter : IAnalogOutputter
{
    public String DictionaryKey { get; set; }
    public double getVoltage(IOutputValueProvider provider)
    {
        return provider.GetDictionaryValue(DictionaryKey);
    }
}

那么,我只需要确保 Acquisition 实现该接口:

class Acquisition : IOutputValueProvider
{
    public Int32 IntegrationTime { get; set; }
    public Double Battery { get; set; }
    public Double Signal { get; set; }
    public Dictionary<String, Double> DictionaryValues;

    public double GetBattery() { return Battery;}
    public double GetSignal() { return Signal; }
    public int GetIntegrationTime() { return IntegrationTime; }
    public double GetDictionaryValue(String key) 
    {
        Double d = 0.0;
        return DictionaryValues.TryGetValue(key, out d) ? d : 0.0;
    }
}

这并不完美,因为现在必须维护一个巨大的接口,并且 Acquisition 中有一些重复的代码,但更改某些内容影响应用程序其他部分的风险要小得多。它还允许我开始子类化Acquisition,而不必更改其中一些外部部分。我希望这会对其他处于类似情况的人有所帮助。

Ok, I hate to not give someone else the credit here, but I found a hybrid solution that works very well for my purposes. It serializes perfectly, and greatly simplifies the addition of new output types. The key was a single interface, IOutputValueProvider. Also note how easily this pattern handles the retrieval of varying ways of storing the data (such as a Dictionary instead of a parameter).

interface IOutputValueProvider
{
    Double GetBattery();
    Double GetSignal();
    Int32 GetIntegrationTime();
    Double GetDictionaryValue(String key);
}

interface IAnalogOutputter
{
    double getVoltage(IOutputValueProvider provider);
}

class BatteryAnalogOutputter : IAnalogOutputter
{
    double getVoltage(IOutputValueProvider provider)
    {
        return provider.GetBattery();
    }
}

class DictionaryValueOutputter : IAnalogOutputter
{
    public String DictionaryKey { get; set; }
    public double getVoltage(IOutputValueProvider provider)
    {
        return provider.GetDictionaryValue(DictionaryKey);
    }
}

So then, I just need to ensure Acquisition implements the interface:

class Acquisition : IOutputValueProvider
{
    public Int32 IntegrationTime { get; set; }
    public Double Battery { get; set; }
    public Double Signal { get; set; }
    public Dictionary<String, Double> DictionaryValues;

    public double GetBattery() { return Battery;}
    public double GetSignal() { return Signal; }
    public int GetIntegrationTime() { return IntegrationTime; }
    public double GetDictionaryValue(String key) 
    {
        Double d = 0.0;
        return DictionaryValues.TryGetValue(key, out d) ? d : 0.0;
    }
}

This isn't perfect, since now there's a gigantic interface that must be maintained and some duplicate code in Acquisition, but there's a heck of a lot less risk of something being changed affecting the other parts of my application. It also allows me to start subclassing Acquisition without having to change some of these external pieces. I hope this will help some others in similar situations.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文