写点什么

依赖倒置和案例

用户头像
王锟
关注
发布于: 2020 年 06 月 16 日

1. 依赖倒置原则,为什么叫好莱坞原则?

 

依赖倒置原则(Dependence Inversion Principle):  

1、高层模块不应该依赖底层模块,二者都应该依赖抽象。

2、抽象不应该依赖细节,细节应该依赖抽象。

3、依赖倒置的中心思想是面向接口编程。

4、依赖倒置原则是基于这样的设计理念:相对于细节的多变性,抽象的东西要稳定的多。以抽象为基础搭建的架构比以细节为基础搭建的架构要稳定的多。

5、使用接口或抽象类的目的是指定好规范,而不涉及任何具体的操作,把展现细节的任务交给他们的实现类来完成。

 

好莱坞原则(Hollywood Principle):别打电话给我们,有事我会打电话给你。好莱坞原则是用在系统的高层组件与底层组件之间。高层组件不应该直接调用底层组件,而是从容器获取。

 

2. 依赖倒置原则的应用

 

实现 Web Service 依赖倒置

分析

相信在实践设计模式的过程中,开发人员已经对依赖倒置的概念有了深刻的体验,“不依赖于具体实现,而是依赖于抽象”,整理 SOA 环境下的 Web Service 一样需要借鉴这个概念,笔者将之称为“Web Service 依赖倒置”。大概逻辑结构变成如下:



图2:概要Web Service依赖倒置后的逻辑关系

 

但Web Service本身接口是“平的”,没有办法继承,只有用OO语言把它进行包装之后才可以成为对应的类,这时候才能有所谓的“继承”或“接口实现”;所谓“抽象”既可能是接口也可能是抽象类(当然,也可以考虑用实体基类),所以在处理ConcreteWebService与抽象Web Service的时候也有两种方式:

l 通过继承的

l 通过单继承+多接口组合的

 

笔者更倾向于后者,因为通过组合可以不断扩展。同时考虑到Web Service使用往往在一个分布式的环境中,因此参考RPC中常用的叫法,增加了一一个Stub(用接口IServiceX表示)和Proxy。修改后依赖倒置的关系如下:

图3:分布式环境下多组合服务接口实现的Web Service依赖倒置

 

实现示例

1、对业务数据建模(XSD):

假设业务对象为报价信息,报价分为报价头和明细(1:0..n),因此结构如下:



图4:报价信息的XSD

XSD

 

<?xml version="1.0" encoding="UTF-8"?>



<xs:schema



  xmlns="http://www.visionlogic.com/trade"



  xmlns:xs="http://www.w3.org/2001/XMLSchema"



  targetNamespace="http://www.visionlogic.com/trade"



  elementFormDefault="qualified"



  attributeFormDefault="unqualified">



    <xs:element name="Quote">



        <xs:annotation>



            <xs:documentation>Comment describing your root element</xs:documentation>



        </xs:annotation>



        <xs:complexType>



            <xs:sequence>



                <xs:element ref="QuoteItem" minOccurs="0" maxOccurs="unbounded"/>



            </xs:sequence>



            <xs:attribute name="Id" type="xs:string" use="required"/>



            <xs:attribute name="Company" type="xs:string" use="required"/>



        </xs:complexType>



    </xs:element>



    <xs:element name="QuoteItem">



        <xs:complexType>



            <xs:attribute name="ProductId" type="xs:integer" use="required"/>



            <xs:attribute name="Price" type="xs:double" use="required"/>



            <xs:attribute name="QuantitiveInStock" type="xs:double"/>



        </xs:complexType>



    </xs:element>



</xs:schema>

2、完成XSD与对象实体的映射:(XSD to Object)

Command

通过Visual Studio.Net自带的Xsd.exe进行如下操作。

 

xsd Quote.xsd /c /n:DemoService

这样就生成了结构大概如下的对应的报价实体类:

C#

 

using System;



using System.Xml.Serialization;



namespace DemoService



{



    [System.SerializableAttribute()]



    [XmlTypeAttribute(AnonymousType = true, Namespace = "http://www.visionlogic.com/trade")]



    [XmlRootAttribute(Namespace = "http://www.visionlogic.com/trade", IsNullable = false)]



    public partial class Quote



    {



        private QuoteItem[] quoteItemField;



        private string idField;



        private string companyField;



        [XmlElementAttribute("QuoteItem")]



        public QuoteItem[] QuoteItem



        {



            get { return this.quoteItemField; }



            set { this.quoteItemField = value; }



        }



        [XmlAttributeAttribute()]



        public string Id



        {



            get { return this.idField; }



            set { this.idField = value; }



        }



        [XmlAttributeAttribute()]



        public string Company



        {



            get { return this.companyField; }



            set { this.companyField = value; }



        }



    }

    [SerializableAttribute()]



    [XmlTypeAttribute(AnonymousType = true, Namespace = "http://www.visionlogic.com/trade")]



    [XmlRootAttribute(Namespace = "http://www.visionlogic.com/trade", IsNullable = false)]



    public partial class QuoteItem



{



… …



    }



}

3、接着,完成抽象的Web Service定义(optional):

该步骤的目的是获取wsdl定义。这里笔者为了省事,用Visual Studio.Net自动生成,所以写了个抽象的Web Service类,实际开发中完全可以独立编写wsdl文件。

C#

 

using System.Web.Services;



using System.Xml.Serialization;



namespace DemoService



{



    [WebService(Name="QuoteService", Namespace="http://www.visionlogic.com/trade")]



    public abstract class QuoteServiceBase : WebService



    {



        [WebMethod()]



        [return:XmlElement("Quote", Namespace="http://www.visoinlogic.com/trade")]



        public abstract Quote GetQuote(string id);



    }



}

WSDL(Quote.wsdl)

 

<?xml version="1.0" encoding="utf-8"?>



<wsdl:definitions xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tm="http://microsoft.com/wsdl/mime/textMatching/" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/" xmlns:tns="http://www.visionlogic.com/trade" xmlns:s1="http://www.visoinlogic.com/trade" xmlns:s="http://www.w3.org/2001/XMLSchema" xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/" xmlns:http="http://schemas.xmlsoap.org/wsdl/http/" targetNamespace="http://www.visionlogic.com/trade" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">



  <wsdl:types>



    <s:schema elementFormDefault="qualified" targetNamespace="http://www.visionlogic.com/trade">



      <s:import namespace="http://www.visoinlogic.com/trade" />



      <s:element name="GetQuote">



        <s:complexType>



          <s:sequence>



            <s:element minOccurs="0" maxOccurs="1" name="id" type="s:string" />



          </s:sequence>



        </s:complexType>



      </s:element>



      <s:element name="GetQuoteResponse">



        <s:complexType>



          <s:sequence>



            <s:element minOccurs="0" maxOccurs="1" ref="s1:Quote" />



          </s:sequence>



        </s:complexType>



      </s:element>



… …



  <wsdl:service name="QuoteService">



    <wsdl:port name="QuoteServiceSoap" binding="tns:QuoteServiceSoap">



      <soap:address location="http://localhost:2401/QuoteServiceBase.asmx" />



    </wsdl:port>



    <wsdl:port name="QuoteServiceSoap12" binding="tns:QuoteServiceSoap12">



      <soap12:address location="http://localhost:2401/QuoteServiceBase.asmx" />



    </wsdl:port>



  </wsdl:service>



</wsdl:definitions>

4、生成Web Service接口类型:

Command

通过Visual Studio.Net自带的Wsdl.exe进行如下操作。

 

wsdl /n:DemoService /serverinterface /o:IQuoteStub.cs Quote.wsdl Quote.xsd

这样就生成了报价Web Service的抽象接口:

C#

 

using System.Web.Services;



using System.Web.Services.Protocols;



using System.Web.Services.Description;



using System.Xml.Serialization;



namespace DemoService



{



    [WebServiceBindingAttribute(



        Name = "QuoteServiceSoap", Namespace = "http://www.visionlogic.com/trade")]



    public interface IQuoteServiceSoap



    {



        [WebMethodAttribute()]



        [SoapDocumentMethodAttribute(



            "http://www.visionlogic.com/trade/GetQuote",



            RequestNamespace = "http://www.visionlogic.com/trade",



            ResponseNamespace = "http://www.visionlogic.com/trade",



            Use = SoapBindingUse.Literal,



            ParameterStyle = SoapParameterStyle.Wrapped)]



        [return: XmlElementAttribute("Quote",



            Namespace = "http://www.visoinlogic.com/trade")]



        Quote GetQuote(string id);



    }



}

5、生成具体的报价Web Service:

为了示例的方便,IntranetQuoteService自己“手捏”了一票测试报价数据,至此服务端Web Service工作基本完成,如果需要使用UDDI则还需要把这个具体服务publish出来。

C#

 

using System;



using System.Web.Services;



using System.Web.Services.Protocols;



namespace DemoService



{



    /// <summary>



    ///具体的报价Web Service功能实现



    /// </summary>



    [WebService(Namespace = "http://www.visionlogic.com/trade")]



    [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]



    public class IntranetQuoteService : WebService, IQuoteServiceSoap



    {



        /// <summary>



        ///实现抽象的Web Service调用



        /// </summary>



        /// <param name="id"></param>



        /// <returns></returns>



        [WebMethod]



        public Quote GetQuote(string id)



        {



            #region "手捏"出来的测试数据



            Quote quote = new Quote();



            quote.Id = id;



            quote.Company = "deluxe";

            QuoteItem[] items = new QuoteItem[2];



            items[0] = new QuoteItem();



            items[0].QuantitiveInStockSpecified = true;



            items[0].ProductId = "Note Bulletin";



            items[0].Price = 220;



            items[0].QuantitiveInStock = 10;



            items[1] = new QuoteItem();



            items[1].QuantitiveInStockSpecified = true;



            items[1].ProductId = "Pen";



            items[1].Price = 3.4;



            items[1].QuantitiveInStock = 3000;



            quote.QuoteItem = items;



            #endregion

            return quote;



        }



    }



}

6、生成客户端Proxy:

Command

通过Visual Studio.Net自带的Wsdl.exe进行如下操作。

 

wsdl /n:Test.Client /o:QuoteProxy.cs Quote.wsdl Quote.xsd

这样就生成了报价Web Service的客户端Proxy,他仅通过最初抽象Web Service的WSDL调用服务端Web Service。实际运行过程中,它并不了解真正使用的时候是由哪个服务提供WSDL中声明到的“GetQuote”方法。

C#

 

using System.Web.Services;



using System.Threading;



using System.Web.Services.Protocols;



using System.Web.Services.Description;



using System.Xml.Serialization;



using DemoService;



namespace Test.Client



{



    /// <summary>



    /// Web Service的客户端Proxy



    /// </summary>



    [WebServiceBindingAttribute(



        Name="QuoteServiceSoap",



        Namespace="http://www.visionlogic.com/trade")]



    public class QuoteService : SoapHttpClientProtocol



    {   



        /// <summary>



        ///借助SOAP消息调用Web Service服务端



        /// </summary>



        /// <param name="id"></param>



        /// <returns></returns>



        [SoapDocumentMethodAttribute(



            "http://www.visionlogic.com/trade/GetQuote",



            RequestNamespace="http://www.visionlogic.com/trade",



            ResponseNamespace="http://www.visionlogic.com/trade",



            Use=SoapBindingUse.Literal,



            ParameterStyle=SoapParameterStyle.Wrapped)]



        [return: XmlElementAttribute("Quote",



            Namespace="http://www.visoinlogic.com/trade")]



        public Quote GetQuote(string id)



        {



            object[] results = this.Invoke("GetQuote", new object[] {id});



            return ((Quote)(results[0]));



        }



    }



}

7、客户程序:

最后,通过单元测试工具检查的客户程序如下:

C#

 

using System;



using DemoService;



using Microsoft.VisualStudio.TestTools.UnitTesting;



namespace Test.Client



{



    /// <summary>



    ///测试用客户程序



    /// </summary>



    [TestClass]



    public class Client



    {



        /// <summary>



        ///为了简化,这里在客户程序中直接定义了具体报价Web Service的Uri.



        ///实际开发中该信息应该作为服务端的一个配置项登记在Directory之中,



        ///客户程序仅仅通过抽象的服务逻辑名称从Directory中获得。)



        /// </summary>



        [TestMethod]



        public void Test()



        {



            QuoteService service = new QuoteService();



            service.Url = "http://localhost:2401/IntranetQuoteService.asmx";



            Quote quote = service.GetQuote("quote:2007-07-15");



            Assert.AreEqual<string>("quote:2007-07-15", quote.Id);



            Assert.AreEqual<string>("deluxe", quote.Company);



            Assert.AreEqual<int>(2, quote.QuoteItem.Length);



            Assert.IsNotNull(quote.QuoteItem[0]);



        }



    }



}

注:为了使用方便,本系列所有示例都没有直接采用IIS作为Web Server宿主,而是采用Visual Studio.Net自带的临时服务进程,因此WSDL和Proxy的使用上,相关端口可能会变化。

进一步改进

上面的示例在客户端处理上不算成功,因为它需要客户程序提供ConcreteService的Uri,怎么改进呢?回忆我们通常对连接串的处置办法:

l 应用逻辑使用一个逻辑的数据库名称,通过一个数据访问框架调用逻辑的数据库。

l 数据访问框架中有一个类似ConnectionManager的机制,负责把逻辑的数据库连接名翻译成实际的连接串。

对上面那个Web Service示例的也如法炮制,增加一个逻辑的Directory机制,实际工程中这个Directory可能就是个UDDI服务,不过这里定义了一个精简对象。

图5:为客户程序增加服务Uri管理目录机制

实现如下

C# IServiceDirectory

 

using System;



namespace Test.Client



{



    /// <summary>



    ///抽象的服务目录接口



    /// </summary>



    public interface IServiceDirectory



    {



        /// <summary>



        ///通过索引器实现按名称或取实际服务Uri的机制。



        ///为了约束客户程序对服务目录的使用,仅提供一个readonly的访问机制。



        /// </summary>



        /// <param name="name">逻辑的服务名称</param>



        /// <returns>实际服务实体的Uri </returns>



        string this[string name] { get;}



    }



}

C# LocalServiceDirectory

 

using System;



using System.Collections.Generic;



using System.Collections.Specialized;



using System.Configuration;



namespace Test.Client



{



    class LocalServiceDirectory : IServiceDirectory



    {



        /// <summary>



        ///保存逻辑服务名称与具体Uri对应关系的目录字典。



        /// </summary>



        private static IDictionary<string, string> dictionary = null;

        /// <summary>



        ///静态构造的过程中,通过访问配置,获取对应关系。



        /// </summary>



        static LocalServiceDirectory()



        {



            NameValueCollection appSettings = ConfigurationManager.AppSettings;



            if ((appSettings == null) || (appSettings.Count <= 0)) return;



            dictionary = new Dictionary<string, string>();



            foreach (string name in appSettings.Keys)



                dictionary.Add(name, appSettings[name]);



        }

        public string this[string name]



        {



            get



            {



                string uri;



                if (!dictionary.TryGetValue(name, out uri))



                    return string.Empty;



                else



                    return uri;



            }



        }



    }



}

C# DirectoryServiceFactory

 

using System;



namespace Test.Client



{



    /// <summary>



    ///为了隔离客户程序对实际DirectoryService类型的以来,引入的服务目录工厂。



    /// </summary>



    public static class DirectoryServiceFactory



    {



        /// <summary>



        ///工厂方法。



        ///世纪项目中,实体ServiceDirectory类型可能运行于远端服务器上,



        ///或者就是UDDI服务,获取IServiceDirectory过程可能还需要借助代理程序完成。



        /// </summary>



        /// <returns></returns>



        public static IServiceDirectory Create()



        {



            return new LocalServiceDirectory();



        }



    }



}

C#修改后的客户程序

 

using System;



using DemoService;



using Microsoft.VisualStudio.TestTools.UnitTesting;



namespace Test.Client



{



    [TestClass]



    public class Client



    {



        [TestMethod]



        public void Test()



        {



            QuoteService service = new QuoteService();



            service.Url = DirectoryServiceFactory.Create()["QuoteService"];



  … …



        }



    }



}

CacheLoader

如果有合理的默认方法来加载或计算与键关联的值。

LoadingCache是附带CacheLoader构建而成的缓存实现。创建自己的CacheLoader通常只需要简单地实现V load(K key) throws Exception方法。

从LoadingCache查询的正规方式是使用get(K)方法。这个方法要么返回已经缓存的值,要么使用CacheLoader向缓存原子地加载新值。由于CacheLoader可能抛出异常,LoadingCache.get(K)也声明为抛出ExecutionException异常。

 

CacheBuilder

缓存构建器。构建缓存的入口,指定缓存配置参数并初始化本地缓存。



主要采用builder的模式,CacheBuilder的每一个方法都返回这个CacheBuilder知道build方法的调用。



注意build方法有重载,带有参数的为构建一个具有数据加载功能的缓存,不带参数的构建一个没有数据加载功能的缓存。

LocalManualCache

作为LocalCache的一个内部类,在构造方法里面会把LocalCache类型的变量传入,并且调用方法时都直接或者间接调用LocalCache里面的方法。



作者:Acamy丶



链接:https://www.jianshu.com/p/38bd5f1cf2f2



来源:简书



著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。



发布于: 2020 年 06 月 16 日阅读数: 77
用户头像

王锟

关注

还未添加个人签名 2018.09.26 加入

还未添加个人简介

评论

发布
暂无评论
依赖倒置和案例