有更好的设计方案吗

本文关键字:方案 更好 | 更新日期: 2023-09-27 18:30:04

免责声明:我很乐意在此使用依赖项注入项目中,并有一个松散耦合的基于接口的全面设计,但在这个项目中,依赖注入的使用被否决了。此外,在我工作的地方,固体设计原则(以及一般的设计模式)是一种陌生的东西,我自己对其中的许多都是新手。所以考虑一下建议对此问题进行更好的设计时应考虑的问题

这是我正在编写的代码的简化版本,因此它可能看起来有些做作。如果是的话,我道歉。考虑以下类别:

// Foo is a class that wraps underlying functionality from another 
// assembly to create a simplified API. Think of this as a service layer class,
// a facade-like wrapper. It contains a helper class that is specific to
// foo. Other AbstractFoo implementations have their own helpers.
public class Foo : AbstractFoo
{
    private readonly DefaultHelper helper;
    public override DefaultHelper Helper { get { return helper; } }
    public Foo()
    {
        helper = new Helper("custom stuff");
    }
    public override void Operation1(string value)
    {
        Console.WriteLine("Operation1 using " + value);
    }
    public override void Operation2()
    {
        Console.WriteLine("Operation2");
    }
}

// Helper derives from a default implementation and allows us to
// override it's methods to do things specific for the class that 
// holds this helper. Sometimes we use a custom helper, sometimes
// we use the default one.
public class Helper : DefaultHelper 
{
    private readonly string customStuff;
    public Helper(string value)
    {
        customStuff = value;
    }
    public override void DoSomethingHelpful()
    {
        Console.WriteLine("I was helpful using " + customStuff);
    }
}

假设这两个类的用法如下:

    // foo referenced and used in one part of code
    var foo = new Foo();
    foo.Operation2(); // or foo.Operation1();
    // some other point in the program where we don't have a reference to foo
    // but do have a reference to the helper
    helper.DoSomethingHelpful();

然而,我现在发现,在helper.DoSomethingHelpful();的一些实现中,我还需要执行foo.Operation1?我想到的潜在解决方案是:

  1. 使foo和helper具有双向关系。因此,在DoSomethingHelpful中,我们可以调用foo。操作2
  2. 让foo实现IHelp接口,并将"helper"代码移动到foo中
  3. 使用委托并将方法Operation2作为Action<string>委托传递到Helper的构造函数中

这些方法似乎都不理想(尽管我已经确定我不喜欢选项1,并且如果我们稍后发现需要传递更多委托,我担心选择3的可维护性)。这让我怀疑Helper/Foo组合的初始设计是否存在问题。想法?

有更好的设计方案吗

随意("使用")关系如何:

public class Helper : DefaultHelper 
{
    private readonly string customStuff;
    public Helper(string value)
    {
        customStuff = value;
    }
    public override void DoSomethingHelpful(AbstractFoo foo)
    {
        foo.Operation1();
        Console.WriteLine("I was helpful using " + customStuff);         
    }
}

因此,您可以修改抽象帮助程序,以期望引用正确的Foo实现。

"这些方法似乎都不理想(尽管我我决定不喜欢选项1,并担心可维护性对于选项3,如果我们稍后发现,我们需要通过更多的代表)。这让我怀疑Helper/Foo组合。"

你说得很对,Helper和Foo的设计有问题。正如您最初描述的那样,基本的Foo/Helper关系很好,当您必须包装其他不受控制的对象时,这是一种常见的模式。但你说:

"如果我发现我也需要在某些情况下执行foo.Operation1helper的实现。DoSomethingHelpful();?"

这就是我们的问题所在。您开始描述Foo依赖Helper的关系;现在您正在描述Helper依赖Foo的关系。这立刻告诉我,你们的依赖关系是纠缠不清的。对象之间的依赖关系应该只朝一个方向发展;事实上,依赖项注入依赖于此。

我认为您已经拥有了所需的东西。尽量不要为"以防万一我以后需要"而设计,也不要修复没有损坏的东西。如果将来需要从助手中使用Operation1,则将其作为对构造函数的依赖项添加(如您所建议的),或者将其传递给您正在调用的方法。这将取决于场景,当你真正需要一些东西时,你就会拥有它。

编辑:更改了"尽量不要为未来设计",因为这似乎不是我想说的。

由于问题发生更改而再次编辑

你可以这样做:

helper.DoSomethingUsefulWith( foo );

因此,您的助手方法将接收到工作所需的依赖项

我认为您的所有解决方案都很好;它们只是提供了不同的功能。这些差异现在并不重要,但在未来很可能会如此。

你更喜欢第二个,你的直觉是最好的指南,你比我们其他人更了解你的代码的未来。我最喜欢你的第二个解决方案,因为它去掉了一个类,更简单。由于它的简单性,如果你以后必须做其他事情,你就不必浪费很多工作。

第一种方法允许您使用不同的Helper(IHelper?)实例和子类玩游戏。最后一种方法为Helper增加了很大的灵活性。(虽然它可能会增加很多内容,但你不需要Helper,只需要你传递给它的方法。)如果其中任何一个似乎能解决未来更多未解决的问题,你可以稍后改用它们。