实现通用行为的替代方案是什么,与抽象基类相关?

7
在C#中,我有一个类层次结构,顶部有几个抽象基类和一些派生类。其中一些具体类具有一些共同的属性和方法,这些方法的实现完全相同。这让我感到浪费,因此一个解决方案可能是在另一个抽象基类中实现这种共同的行为。
abstract class Control;

abstract class SquareControl: Control
{
    public int SquarishProperty;
    public void SquarishMethod();
};

class Window: SquareControl;
class Button: SquareControl;

然而,如果继承层次结构中的其他几个类共享某些其他行为,但同时与另一个基类的控件共享某些相似之处怎么办?也许有很多共性。使用抽象基类实现来建模这种情况将变得不切实际,是吧?

abstract class FlashableControl: Control
{
    public int FlashyProperty;
    public void FlashMethod();
};

class StatusBar: FlashableControl;  // but it's also a bit square too, hmm...

那么如何在不使用基类的情况下跨类共享这样的实现呢?
我想象我想将接口的实现委托给另一个类,并让该类代表所需的类实现这些属性和方法,以便对用户而言,StatusBar和Window似乎支持标准接口,但在内部,它是由其他实现它的东西来实现的。
我可以想象聚合实现此行为的类,但这是否适当,有什么缺点?还有其他选择吗?
谢谢

FlashMethod和SquarishProperty等,在基类中是否有具体实现,还是抽象的? - SWeko
这里它们是抽象的,以说明我不想做什么,在实际实现中,我有许多派生类,其中一些共享相同的实现,不仅是接口,而且实际代码(属性和方法)也是相同的。 - DaBozUK
3个回答

3
你可以使用“有一个”代替“是一个”,并委托给内部方形控件。
    class Window : Control, ISquareControl
    {
        private SquareControl square;

        public void SquareOperation()
        {
            square.SquareOperation();
        }
    }

    class SquareControl : Control, ISquareControl
    {
        public void SquareOperation()
        {
            // ...
        }
    }

这个聚合方法基本上是我拟定的计划,也许我想把SquareControl类设置为内部类,以便于对Window用户进行隐藏。在这种方法中是否存在任何陷阱、限制或者难点呢? - DaBozUK

3
您可以使用以下模式:

您可以使用这样的模式:

public interface ICommonServices
{
    string SomeProperty { get; set; }

    void SomeMethod(string param);
}

public static class CommonServiceMethods
{
    public static void DoSomething(this ICommonServices services, string param)
    {
        services.SomeMethod(services.SomeProperty + ": " + param + " something extra!");
    }
}

所有实现 ICommonServices 接口的类都可以通过扩展方法获得一些免费的行为,该方法仅依赖于所有 ICommonServices 实现者公开的功能。如果您需要访问基类功能,则可以将其放在自己的接口中,并使 ICommonServices 实现该接口。现在,您可以为接口创建“默认”扩展功能,而无需使用多个基类。

编辑

如果您希望其中一些方法是 internal,您可以修改模式如下:

public class MyObject : IServices
{
    public string PublicProperty { get; private set; }

    string IServices.SomeProperty { get; set; }

    void IServices.SomeMethod(string param)
    {
        //Do something...
    }
}

public interface IPublicServices
{
    string PublicProperty { get; }
}

internal interface IServices : IPublicServices
{
    string SomeProperty { get; set; }

    void SomeMethod(string param);
}

internal static class ServiceMethods
{
    public static void DoSomething(this IServices services, string param)
    {
        services.SomeMethod(services.SomeProperty + ": " + param + " something extra!");
    }
}

基本上,我们同时暴露公共接口和内部接口。请注意,我们明确实现了内部接口方法,以便这些方法不可供公共使用(因为公共客户端无法访问接口类型)。在这种情况下,帮助扩展方法是内部的,依赖于内部接口,尽管您也可以创建依赖于公共接口的公共帮助程序方法。


我非常喜欢这种方法,将尝试实现它。在其他程序集的客户端代码中隐藏这个类的最佳方法是什么? Boz - DaBozUK
@DaBozUK,我已经修改了答案,并提供了一个示例,说明如何仅提供内部访问。 - Dan Bryant
顺便提一下,显式实现意味着如果您拥有类本身的实例(而不是接口),则这些方法将对内部客户端不可用。您可以通过创建具有相同名称的内部方法来解决此问题,该方法会调用显式接口覆盖的方法。 - Dan Bryant

0

一种方法是使用接口和基类。

Flashable 可以作为一个良好的接口而不是类。


接口和基类基本上就是我上面所描述的,而这也正是我想避免的。问题在于,如果几个不同的类集共享某些但不是全部行为,那么类层次结构可能会变得混乱无序。 - DaBozUK

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接