28

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

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
        }
   }   
}

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

4

7 回答 7

21

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

public interface IMean : IDisposable
{
    ...
}

但是,如果让一个对象实现 IMean 而不是一次性的,那么我认为您建议的解决方案是最好的:创建一个中间接口,这样您就可以拥有:

public interface IMean
{
    ...
}

public interface IDisposableMean : IMean, IDisposable
{
    ...
}
于 2013-04-23T09:59:00.800 回答
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
        }
   }   
}
于 2013-04-23T09:52:41.117 回答
7

T您还可以引入必须实现多个接口的泛型类型。这是一个使用IFooand的示例IDisposable

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是错误的,这可能是有道理的。

于 2013-04-23T09:54:37.293 回答
5

When you have code that requires a type implements multiple different interfaces that's exactly what you usually have to do. But there are quite a few variations on what could happen, depending on the semantics of your code.

For example, your own proposed solution is acceptable if IMean is not necessarily IDisposable but there are many consumers that do require their IMean to be disposable. You could also use an abstract base class to do this -- "program to an interface" does not use "interface" as in "the language construct defined by the interface keyword" but rather "an abstract version of the object".

In fact you could require that any types you consume implement ITry (and thus are disposable) and simply document that it's fine for some types to implement Dispose as a no-op. If consuming an abstract base class you could also provide this no-op implementation as default.

Another solution would be to use generics:

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

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

Still another case would be a consumer that strictly requires only IMean, but also needs to be disposable-aware. You could handle this by fishing for types:

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

While this is an acceptable practical solution (especially given that IDisposable is "not just any interface") you should definitely take a step back if you find yourself doing this again and again; in general, any form of "type switching" is considered bad practice.

于 2013-04-23T09:56:16.170 回答
4

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

例如

// 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(您仍然可以使用IMeanrefs)

于 2013-04-23T09:58:58.427 回答
2

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

于 2013-04-23T12:32:00.840 回答
1

在这种情况下,您的替代方案似乎有点人为。让 IMean “实现” IDisposable。针对类进行编程并非每次都不好,我取决于情况。

于 2013-04-23T09:55:15.717 回答