了解 C# 中的组合

Understanding composition in C#

我想更好地理解组合而不是继承。我看了一些教程,这些教程很好地解释了这些概念,但使用的是我更熟悉的语言,例如 Python。但是,我目前正在学习 C#,并且正在努力实施我在该主题上学到的知识,想知道是否有人可以提供帮助。这是我的代码:

using System;

namespace Composition
{
    class Program
    {
        static void Main(string[] args)
        {
            SuperBot steven = new SuperBot(new Dog(),new Robot());
            steven.Bark();


        }
    }

    class Dog
    {
        public void Bark()
        {
            Console.WriteLine("Woof");
        }

    }

    class Robot
    {
        public void Move()
        {
            Console.WriteLine("I'm moving!");
        }
    }

    class CleanRobot
    {
        public void Clean()
        {
            Console.WriteLine("Just keep dusting, just keep dusting");
        }
    }

    class SuperBot
    {
        // can clean, move and bark
        public Dog o1;
        public Robot o2;
        public CleanRobot o3;

        public SuperBot(Dog dog, Robot robot)
        {
            this.o1 = dog;
            this.o2 = robot;

        }

    }

}

基本上,我有一个 Dog 可以 bark,一个 Robot 可以 move 和一个 CleanRobot 可以 clean .我想创建一个可以移动、吠叫和清洁的 SuperBot,并使用合成来实现这一目标。

我有两个主要问题:

  1. 以上是我到目前为止编写的代码,但它不允许我在我的 SuperBot 对象上调用 Bark 方法,正如它所说 SuperBot class 不包含此方法。

  2. 如果我的 Dog class 也可以 Eat() 但我不想将此方法用于我的 SuperBotclass?

非常感谢任何帮助 - 努力解决这个问题!

更新 - 尝试使用接口

using System;

namespace Composition
{
    public interface ICanBark
    {
        string Bark();
    }

    public interface ICanMove
    {
        string Move();
    }

    public interface ICanClean
    {
        string Clean();
    }

    class Program
    {


    static void Main(string[] args)
        {
            SuperBot steven = new SuperBot();
            steven.Bark();

        }
    }

    class Dog : ICanBark 
    {
        public string Bark()
        {
            return "Woof"; 
        }
    }

    class Robot : ICanMove
    {
        public string Move()
        {
           return "I'm moving";
        }
    }

    class CleanRobot : ICanClean
    {
        public string Clean()
        {
           return "Just keep dusting, just keep dusting";
        }
    }

    class SuperBot : ICanBark, ICanMove, ICanClean
    { 
        public string Bark()
        {
            return "Barking";
        }
    }

}

我认为 SuperBot 是一个 aggregration type object composition,但我不是 100% 相信那些深入理论领域的术语。

你的描述听起来像是你想做多重继承。不幸的是,那个导致 Diamond Problem。这是 .NET 开发人员想要避免的众多问题之一。于是他们宣布:"Single Inheitance only"。 (注意C#完全支持Multiple Inheritance,但这里Framework规则更重要)

.NET 确实有一些类似于多重继承的东西:Interfaces。没有更好的术语,接口是 "more abstract then a abstract class"。接口不是继承的,而是实现的。由于它们不携带任何代码,您可以实现任意数量的接口,而不必冒可怕的死亡钻石风险。

在您的情况下,您可能需要接口 ICanBark、ICanMove 和 ICanClean。具体的 Dog、Robot 和 Cleaner 类 实现了其中之一。 SuperBot 全部 3.

在您的示例中使用合成时,不幸的是,您还必须在合成中实现暴露的方法 class:

class SuperBot
{
    // can clean, move and bark
    public Dog o1;
    public Robot o2;
    public CleanRobot o3;

    public SuperBot(Dog dog, Robot robot, CleanRobot cleanRobot)
    {
        this.o1 = dog;
        this.o2 = robot;
        this.o3 = cleanRobot;
    }

    public void Bark() => o1.Bark();
    public void Move() => o2.Move();
    public void Clean() => o3.Clean();
}

这将解决您的编译器错误。此外,如果您向 DogRobotCleanRobot 添加更多方法,它们将不会影响 SuperBot 提供的内容,即如果 Dog 具有方法 Eat(), SuperBot 不会有这个方法,除非你也加上它。

如果您将 SuperBot 建模为实际上由 DogRobotCleanRobot 组成,那么从概念上讲,您可以像这样使用组合。 SuperBot 然后用它的 Dog 来吠叫,用它的 CleanRobot 来清洁。


编辑(来自评论):

如果你想要一个调用Bark

的函数
    public void GoodBoy(Dog dog)
    {
        dog.Bark();
    }

您不能将 SuperRobot 传递给此方法。在这种情况下,您需要 DogSuperRobot 的通用接口,其中包含 Bark 方法。