zoukankan      html  css  js  c++  java
  • 从高耦合到低耦合到底有多远?

    一切都是拥抱变化,反过来说,如果没有变化或者需求很稳定,那么一切就是过度设计。所以,一切都要看情况,回到了马克思主义的辩证学。呵呵。

    无论书还是博客, 耦合这个词已被无数人说烂,任何一位程序员都会告诉你设计软件要注意低耦合,可究竟什么是低耦合?每次去查这个问题,就会牵扯出各种术语和理论,让人头晕。最近看了一些英文资料,发现低耦合其实没那么复杂。

    什么是耦合?怎样的代码叫高耦合?

    “耦合”翻译自英文(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的实现类就像工厂模式,通过参数或配置文件等决定生成哪个类。然而这种做法让IServiceLocator和IMessageService 的实现类之间增加了耦合,每添加一个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>

  • 相关阅读:
    微软 软件的 一组堆成快捷键
    C#事件 的讲解
    软件缺陷分析的几种方法
    一个长三角人对深圳的看法 (转)
    一次LoadRunner的CPC考试经历
    测试是一门武功
    ORACLE的性能测试经验总结
    深圳测试协会第九次论坛在深圳举行
    10月28日参加了IBM的产品推介会
    什么是web安全性测试?
  • 原文地址:https://www.cnblogs.com/aaa6818162/p/4499337.html
Copyright © 2011-2022 走看看