针对多个接口编程

时间:2013-04-23 09:48:00

标签: c# oop

我非常喜欢这样的提示:“针对接口的程序,而不是实现”,我试图始终如一地遵循它。但是,当我必须将我的代码与必须从多个接口继承的对象分离时,我怀疑如何保持这个原则。典型的例子可能是:

namespace ProgramAgainstInterfaces
{
    interface IMean
    {
            void foo();
    }  

    class Meaning : IMean , IDisposable
    {
        public void Dispose()
        {
            Console .WriteLine("Disposing..." );
        }

        public void foo()
        {
            Console .WriteLine("Doing something..." );           
        }
    }

   class DoingSomething
   {
        static void Main( string[] args)
        {
            IMean ThisMeaning = (IMean ) new Meaning ();  // Here the issue: I am losing the IDisposable methods
            ThisMeaning.foo();
            ThisMeaning.Dispose();                     // Error: i cannot call this method losing functionality
        }
   }   
}

解决这个问题的一种可能方法是定义一个从两个接口继承的ad-hoc接口:

namespace ProgramAgainstInterfaces
{
    interface IMean
    {
            void foo();
    }

    interface ITry : IMean , IDisposable
    {
    }

    class Meaning : ITry
    {
        public void Dispose()
        {
            Console .WriteLine("Disposing..." );
        }

        public void foo()
        {
            Console .WriteLine("Doing something..." );           
        }
    }

   class DoingSomething
   {
        static void Main( string[] args)
        {
            ITry ThisMeaning = (ITry ) new Meaning ();  // This works
            ThisMeaning.foo();
            ThisMeaning.Dispose();   // The method is available
        }
   }   
}

但我不确定这是否是更紧凑和有效的解决方案:我可能有更复杂的多继承层次结构,这增加了复杂性,因为我必须创建接口才能充当容器。有更好的设计解决方案吗?

7 个答案:

答案 0 :(得分:21)

如果作为“IMean”对象涉及始终是一次性的,那么你应该让接口实现它:

public interface IMean : IDisposable
{
    ...
}

但是,如果有一个对象实现IMean而不是一次性的意义,那么我认为你建议的解决方案是最好的:创建一个中间接口,所以你可能有:

public interface IMean
{
    ...
}

public interface IDisposableMean : IMean, IDisposable
{
    ...
}

答案 1 :(得分:10)

您应该interface实施IDisposable 而不是Meaning。这种方式在投射到interface时,您不会失去IDisposable能力(因为它是在interface级别定义的)。

像这样:

namespace ProgramAgainstInterfaces
{
    interface IMean : IDisposable
    {
        void foo();
    }

    interface ITry : IMean
    {
    }

    class Meaning : ITry
    {
        public void Dispose()
        {
            Console .WriteLine("Disposing..." );
        }

        public void foo()
        {
            Console .WriteLine("Doing something..." );           
        }
    }

   class DoingSomething
   {
        static void Main( string[] args)
        {
            ITry ThisMeaning = (ITry ) new Meaning ();  // This works
            ThisMeaning.foo();
            ThisMeaning.Dispose();   // The method is available
        }
   }   
}

答案 2 :(得分:7)

您还可以引入必须实现多个接口的泛型类型T。以下是使用IFooIDisposable的示例:

class Program
{
    static void Main(string[] args)
    {
    }

    interface IFoo
    {
        void Foo();
    }

    class Bar<T> where T : IFoo, IDisposable
    {
        public Bar(T foo)
        {
            foo.Foo();
            foo.Dispose();
        }
    }
}

这有点复杂。从设计的角度来看,IFoo : IDisposable 错误可能有意义

答案 3 :(得分:5)

当您拥有需要类型的代码实现多个不同的接口时,这正是您通常必须执行的操作。但是根据代码的语义,可能发生的变化有很多变化。

例如,如果IMean不一定是IDisposable,但是有许多消费者确实要求他们的IMean是一次性的,那么您自己建议的解决方案是可以接受的。您还可以使用抽象基类来执行此操作 - “程序到接口”不使用“接口”,如“interface关键字定义的语言构造”,而是“对象的抽象版本” ”

事实上,您可以要求您使用的任何类型都实现ITry(因此是一次性的),并且只是记录一些类型实现Dispose作为无操作的方法。如果使用抽象基类,您也可以将此无操作实现作为默认实现。

另一种解决方案是使用泛型:

void UseDisposableMeaning<T>(T meaning) where T : IMean, IDisposable
{
    meaning.foo();
    meaning.Dispose();
}

// This allows you to transparently write UseDisposableMeaning(new Meaning());

另一种情况是消费者严格要求IMean,但也需要一次性感知。你可以通过钓鱼来处理这个类型:

IMean mean = new Meaning();
var disposable = mean as IDisposable;
if (disposable != null) disposable.Dispose();

虽然这是一个可接受的实用解决方案(特别是考虑到IDisposable“不只是任何界面”)如果你发现自己一次又一次地这样做,你肯定会后退一步;一般来说,任何形式的“类型转换”都被视为不良行为。

答案 4 :(得分:4)

为了构建,您可以通过强制转换检查对象是否支持特定的功能(接口):

e.g。

// Try and cast
var disposable = ThisMeaning as IDisposable; 

// If the cast succeeded you can safely call the interface methods
if(disposable != null) 
    disposable.Dispose();

这意味着您在运行时发现实现而不必在编译时了解它们,并且您的类型不需要 来实现IDisposable

这满足了一次性要求,而不必知道IMean属于Meaning类型(您仍然可以使用IMean refs)

答案 5 :(得分:2)

不实现接口的代码 - “接口”是一个通用术语,它并不意味着字面意思是关键字interface。使用abstract类遵循该原则。如果你需要一些实现,也许更好。此外,子类总是可以根据需要实现interface

答案 6 :(得分:1)

在这种情况下,您的替代方案似乎有点人为。让IMean“实现”IDisposable。针对课程的编程并非每次都很糟糕,我依赖于这种情况。