從高耦合到低耦合到底有多遠?


無論書還是博客, 耦合這個詞已被無數人說爛,任何一位程序員都會告訴你設計軟件要注意低耦合,可究竟什么是低耦合?每次去查這個問題,就會牽扯出各種術語和理論,讓人頭暈。最近看了一些英文資料,發現低耦合其實沒那么復雜。

什么是耦合?怎樣的代碼叫高耦合?

“耦合”翻譯自英文(coupling),英文描述是:"when a component has a dependency on something else". 這句話簡單易懂--當一個組件對其他東西有依賴就叫耦合,為方便描述,先給段代碼:

public class EmailService
{
    public void SendMessage() { }
}

public class NotificationSystem
{
    private EmailService svc;

    public NotificationSystem()
    {
        svc = new EmailService();
    }

    public void InterestingEventHappend()
    {
        svc.SendMessage();
    }
}

 

代碼的邏輯很簡單:NotificationSystem通過內置的EmailService類發送郵件,即NotificationSystem的功能依賴EmailService類。我相信應有不少人對代碼感覺親切,我參與過的項目基本都這種風格,可惜這就是高耦合的設計。

高耦合翻譯自“tightly coupled”,描述是這樣的:"A class that knows a lot about the other classes it interacts with is said to be tightly coupled".翻譯過來就是---它知道的太多了。^_^

最快的解耦方式

為了讓它知道的不那么多,現在貼一份改良后的代碼:

public interface IMessageService 
    {
        void SendMessage();
    }

    public class EmailService : IMessageService
    {
        public void SendMessage() { }
    }

    public class NotificationSystem
    {
        private IMessageService svc;

        public NotificationSystem()
        {
            svc = new EmailService();
        }

        public void InterestingEventHappend()
        {
            svc.SendMessage();
        }
    }

 

與之前比較,svc變量類型變成了接口IMessageService ,從而使NotificationSystem依賴IMessageService接口,而不是EmailService類。 但svc通過new 方式賦值,這讓兩個類藕斷絲連,一旦EmailService變化,NotificationSystem也跟着變,違背了開閉原則。

通過控制反轉徹底解耦

想徹底解耦,就要換一種方式對svc賦值,於是想到控制反轉模式,控制反轉翻譯自“inversion of control”簡稱Ioc,一句話描述:“Moving the creation of dependencies outside of the class that consumes those dependencies”,簡單翻譯過來就是:在外面創建這個類。

現在我們先抽象一個接口用於“外部創建”。

 

public interface IMessageService 
    {
        void SendMessage();
    }

    public class EmailService : IMessageService
    {
        public void SendMessage() { }
    }

    public interface IServiceLocator
    {
        IMessageService GetMessageService();
    }

    public class NotificationSystem
    {
        private IMessageService svc;

        public NotificationSystem(IServiceLocator locator)
        {
            svc = locator.GetMessageService();
        }

        public void InterestingEventHappend()
        {
            svc.SendMessage();
        }
    }

 

從代碼看出,現在svc是通過IServiceLocator接口創建,從而讓原類之間解耦。IServiceLocator的實現類就像工廠模式,通過參數或配置文件等決定生成哪個類。然而這種做法讓IServiceLocatorIMessageService 的實現類之間增加了耦合,每添加一個IMessageService 的實現類,就要修改IServiceLocator的代碼,可能是switch或連續的if,這樣看似不錯的模式仍然違反開閉原則:

public class ServiceLocator:IServiceLocator
    {
        public IMessageService GetMessageService()
        {
            string type = "type1";
            switch (type)
            {
                case "type1":return new EmailService1();
                case "type2": return new EmailService2();
                case "type3": return new EmailService3();
                   …………
            }
        }
    }

 

用Ioc容器完成高耦合到低耦合的蛻變

完全蛻變,就要求助於依賴注入了,這個詞和控制反轉是好基友,一般都同時出現。實際開發中,我們往往使用IoC容器來實現依賴注入的需求。通過Ioc容器(以Autofac為例)改善代碼如下:

public class NotificationSystem
    {
        private IMessageService svc;
        
        public NotificationSystem(IMessageService messageService)
        {
            svc = messageService;
        }

        public void InterestingEventHappend()
        {
            svc.SendMessage();
        }
    }

可以看到NotificationSystem的構造函數直接傳入IMessageService接口變量做參數。在全局類的代碼如下:

var builder = new ContainerBuilder();
   builder.RegisterType<EmailService>().As<IMessageService>();

通過依賴注入來實例化NotificationSystem類:

IMessageService messageService= container.Resolve<IMessageService>();
   NotificationSystem system=new NotificationSystem(messageService);

借助Ioc的幫助,當IMessageService添加新的實現類時,也不用修改其他類的內部代碼,在配置代碼中,僅修改類名便可實現功能的切換。

結語

現在很多的開源代碼都是這種模式,類內部依賴接口,通過Ioc容器靈活配置,熟悉了這種模式,有助於理解別人的設計意圖,我也從中收益良多。但也有讓我不爽的地方就是看別人的代碼時,每次用F12跟蹤源碼,都會跳轉到描述接口的代碼文件中,想看具體實現總要害我找半天。

參考資料:<Professional Asp.Net MVC 3>


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM