65.9K
CodeProject 正在变化。 阅读更多。
Home

SOLID 原则,用外行人的话来说:接口隔离

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.82/5 (5投票s)

2014 年 2 月 10 日

CPOL

4分钟阅读

viewsIcon

15234

SOLID 原则,用外行人的话来说:接口隔离

存在理由:我开始撰写关于 SOLID 软件开发原则的文章。具体来说,我的目标是让其他开发人员更容易理解这些原则,他们(就像我一样)发现不得不解读关于这个问题的冗长、复杂的文章和书籍很麻烦。这些原则供所有人学习和使用,但我发现它们很难完全掌握;很可能因为我来自非英语背景。因此,通过本系列文章,我打算尝试解开这些原则的神秘面纱,并抱有最好的意图。这些原则适用于软件开发的许多层面。在我的文章中,我特别致力于描述它们与编程的关系。我希望它对您有所帮助。感谢您的光临。

这将是一个关于 SOLID 的 5 篇文章系列。SOLID 正在风靡一时;至少从我正在阅读的招聘广告来看是这样;“您需要遵守 SOLID 原则”等等。那么,SOLID 究竟是什么?简单来说,它是在开发面向对象系统时的一组指导方针。它们是一组概念,已被证明对许多编写大量软件的人来说非常有价值。如果您愿意,这是软件工程领域的长辈们讲述的故事,您需要注意它,这样您就可以在您的简历上夸耀您正在研究 SOLID 原则,并且您将成为一个更好的开发人员,因为您了解它们,我向您保证。 事实上,您仅仅是_想_了解它们就让您成为了一个更好的开发人员!

SOL[I]D - 接口隔离原则

我们关于 SOLID 原则的文章系列的第 4 部分带我们了解了接口隔离。我们再次求助于维基百科以获取官方解释,它声明,[引用] “不应强迫任何客户端依赖它不使用的方法。” [/引用]。让我们深入了解一下 - 那么,这一切都与什么有关呢?

真的很简单:基本上,这个原则意味着,如果您正在实现接口并且您的实现没有实现该接口的所有方法,那么这表明您的接口臃肿,可能需要重新设计。

先举一个简短的例子。同样,这些都是非常简单的例子,仅用于让您掌握这个概念。假设我们有这个接口,我们希望实现它,以及两个这样的实现

  public interface ILogLocation
    {
        string LogName { get; set; }
        void Log(string message);
        void ChangeLogLocation(string location);
    }

     public class DiskLogLocation : ILogLocation
    {
        public string LogName { get; set; }

         public DiskLogLocation(string _logName)
        {
            LogName = _logName;
        }

         public void Log(string message)
        {
            // do something to log to  a file here
        }

         public void ChangeLogLocation(string location)
        {
            // do something to change to a new log-file path here
        }

     }

     public class EventLogLocation : ILogLocation
    {
        public string LogName { get; set; }

         public EventLogLocation()
        {
              LogName = "WindowsEventLog";
        }

         public void Log(string message)
        {
            // do something to log to the event-viewer here
        }

 
         public void ChangeLogLocation(string location)
        {
            // we can't change the location of the windows event log, 
            // so we'll simply return
            return;
        }
    }

上面是 ILogLocation 接口的两个实现。两者都实现了 ChangeLogLocation() 方法,但只有我们的 DiskLogLocation 实现为我们带来了有意义的东西。这很混乱;就像把一个冬天的毛线帽戴在你的夏帽上,它们都是帽子,但你不需要同时戴着它们。假设我们有三个 DiskLogLocations 和一个 EventLogLocation:现在,出于配置目的,我们可以通过使用它们的接口来遍历它们,这将是一个完全合理的事情...

var logLocations = new List<iloglocation>() {
                new DiskLogLocation("DiskWriteLog"),
                new DiskLogLocation("DiskReadLog"),
                new EventLogLocation(),
                new DiskLogLocation("FileCheckLog")
            };

             foreach( var logLocation in logLocations)
            {
                logLocation.ChangeLogLocation(@"c:\" + logLocation.LogName + ".txt");
            }</iloglocation>

...所以我们在上面做了,但正如您所看到的,这导致对 EventLogLocation 对象的 ChangeLogLocation() 的不必要调用。这是不必要的,它不会导致任何结果,而是有可能引入错误,应该被视为代码异味,并且通常“不应该被调用” - 原谅这个双关语。

所以我们可以这样来做

public interface ILogLocation
{
    string LogName { get; set; }
    void Log(string message);
}

 public interface ILogLocationChanger
{
    void ChangeLogLocation(string location);
}

 public class DiskLogLocation : ILogLocation,  ILogLocationChanger
{
    public string LogName { get; set; }

     public DiskLogLocation(string _logName)
    {
        LogName = _logName;
    }

     public void Log(string message)
    {
        // do something to log to  a file here
    }

     public void ChangeLogLocation(string location)
    {
        // do something to change to a new log-file path here
    }
}

 public class EventLogLocation : ILogLocation
{
    public string LogName { get; set; }

     public EventLogLocation()
    {
        LogName = "WindowsEventLog";
    }

     public void Log(string message)
    {
        // do something to log to the event-viewer here
    }
}

在上面,我们将 ChangeLogLocation() 方法移动到它自己的接口中,ILogLocationChanger - 仅由 DiskLogLocation 实现。这是一种更好的方法;再也不会有不必要的方法实现!此外,我们现在可以通过它们实现的抽象来区分实现。我们可以潜在地重用一个或多个接口用于其他类。

所以这就是它 - 接口隔离原则,它基本上意味着“如果你发现你必须实现你的类不需要的方法,或者你只是返回,或者抛出异常,那么就拆分你的接口”。简单!

顺便说一句,您可能会思考这个接口隔离原则与单一职责原则的区别;作为提醒,单一职责原则告诉我们一个模块应该只承担单一的职责。这与我们刚刚接受的这个接口隔离原则不是一回事吗?嗯,它们有些相似。存在差异,但我不会深入探讨。相反,我将参考 StackOverflow 用户 Andreas Hallberg,我认为他撰写的解释比我能想到的任何解释都更好:(参考:http://stackoverflow.com/questions/8099010/is-interface-segregation-principle-only-a-substitue-for-single-responsibility-pr)[引用]“以一个类的例子为例,其职责是将数据持久化到例如硬盘驱动器。将类分成读写部分没有实际意义。但是一些客户端应该只使用该类来读取数据,一些客户端只写入数据,还有一些两者都做。在这里应用 ISP 具有三个不同的接口,这将是一个很好的解决方案。”[/引用] 太棒了!所以我们了解到,SR 原则关乎“一件事,而且只是一件事”与 IS 原则关乎“我们需要什么,而且只需要我们需要的东西”。相关的原则,都有效,都很重要。

© . All rights reserved.