• 设计模式备忘录(1):适配器模式、依赖注入依赖倒置、空对象模式


    鸟随凤鸾,人伴贤良,得以共之,我之幸也。说的是鸟随着鸾凤可以飞的更高远,人和比自己境界高的相处,自己也会得到熏染进步。

    一、概述

    分享出来简单的心得,望探讨

    依赖倒置

    依赖注入

    Adapter模式

    Null模式

    二、快速示例

    从一个简单的示例开始,业务流程Client通过Service查询产品,基础大概框架如下

     

    关键代码:

     1 class ProductService
     2     {
     3 
     4         private ProductRepository _productRepository;
     5 
     6         public ProductService()
     7         {
     8             _productRepository=new ProductRepository();
     9         }
    10 
    11         public IList<Product> GerAllProductsIn(int category)
    12         {
    13             IList<Product> products;
    14             string storagekey = string.Format("product_category_id_{0}",category);
    15             products =(List<Product>) HttpContext.Current.Cache.Get(storagekey);
    16 
    17             if (products==null)
    18             {
    19                 products=_productRepository.GetAllProductsIn(category);
    20                 HttpContext.Current.Cache.Insert(storagekey,products);
    21             }
    22             return products;
    23         } 
    24     }
    ProductService

    ProductService直接依赖于ProductRepostory属于强耦合。如何理解这种强耦合呢?现在假设客户新产品上架,需要将ProductRepository替换为NewProductRepository,那么就势必要修改ProductService以及所有引用类ProductRepository,也就是高层模块依赖于低层模块。

     三、重构

    1、依赖倒置

    那么究竟何为依赖倒置,它又如何解耦呢。

    依赖倒置实际就是就是要依赖于抽象,不要依赖于具体。意思就是面对抽象进行,而不是面对实现进行编程,通过抽象解决客户与实现模块间的耦合关系。依赖倒置两大重要的原则:一是高层模块不应依赖于低层模块,都应依赖于抽象,二是具体实现也应依赖于抽象。

    来看怎么做,定义IProductRepostory接口,使ProductRepostory依赖于接口 ,对ProductService重构也依赖于接口

     1 class ProductService
     2     {
     3 
     4         private IProductRepository _productRepository;
     5 
     6         public ProductService()
     7         {
     8             _productRepository=new ProductRepository();
     9         }
    10 
    11         public IList<Product> GerAllProductsIn(int category)
    12         {
    13             IList<Product> products;
    14             string storagekey = string.Format("product_category_id_{0}",category);
    15             products =(List<Product>) HttpContext.Current.Cache.Get(storagekey);
    16 
    17             if (products==null)
    18             {
    19                 products=_productRepository.GetAllProductsIn(category);
    20                 HttpContext.Current.Cache.Insert(storagekey,products);
    21             }
    22             return products;
    23         } 
    24     }
    ProductService

    依赖倒置通过面对抽象从数据类型降低了耦合度,依赖倒置使高层模块依赖于抽象,即只关心new出来的是否是IProductRepository接口的对象而不关心new出来是谁。这样现在需要解决的就是具体实现的创建问题。

    2、依赖注入

    依赖注入从其初衷的角度解释就是:当类A完成某个工作需要创建B类的实例来协助的时候,把创建协助类B的工作提取出来从外部完成。

    依赖注入三种形式:构造器,方法,属性。本文用构造器注入的方式介绍依赖注入

    ProductService重构:

     1 public class ProductService
     2     {
     3         private IProductRepository _productRepository;
     4        
     5 
     6         public ProductService(IProductRepository productRepository)
     7         {           
     8             _productRepository = productRepository;
     9             
    10         }
    11     
    12       ..................
    13 
    14          
    15     }
    ProductService

    测试代码(即外部):

     1 namespace Patterns.QuickDemp.Tests
     2 {
     3     [TestFixture]
     4     public class ProductServiceTests
     5     {
     6         [Test]
     7         public void Test_Client()
     8         {
     9              //外部创建对象,任何继承抽象的对象皆可注入
    10             AnyProductRepository anyproductrepository = new AnyProductRepository(); 
    11             //注入
    12             ProductService productService = new ProductService(anyproductrepository);
    13             ...........
    14             ..........
    15 
    16         }
    17 
    18 
    19     }
    20 }
    Tests

     可以大胆的作如下设想:客户,商家,第三方支付的关系拿来类比,客户直接与商家交易是一种模式。现在依赖反转,银行卡,支付宝,现金券等支付方式全部继承某个接口,支付动作抽象出来,客户选择支付方式然后外部注入,第三方根据注入指定方式完成支付动作。

    3、Adapter模式

     adapter模式意图描述:将一个类的接口转换成客户希望的接口

    问题描述:用户自定义接口ICacheStorage.面向抽象过程中要让HttpContext成为一个实现用户自定义接口的实现。HttpContext是非用户编写的类,所以用到适配器模式

    1 public interface ICacheStorage
    2     {
    3         void Remove(string key);
    4         void Store(string key, object data);
    5         T Retrieve<T>(string storageKey);
    6     }
    ICacheStorage
     1 public class HttpContextCacheAdapter : ICacheStorage
     2     {        
     3         public void Remove(string key)
     4         {
     5             HttpContext.Current.Cache.Remove(key);   
     6         }
     7 
     8         public void Store(string key, object data)
     9         {
    10             HttpContext.Current.Cache.Insert(key, data);    
    11         }
    12 
    13         public T Retrieve<T>(string key)
    14         {
    15             T itemStored = (T)HttpContext.Current.Cache.Get(key);
    16             if (itemStored == null)
    17                 itemStored = default(T);
    18 
    19             return itemStored;       
    20         }       
    21     }
    HttpContextCacheAdapter

    仍然依赖倒置依赖注入对Service重构如下:

     1 public class ProductService
     2     {
     3         private IProductRepository _productRepository;
     4         private ICacheStorage _cacheStorage;
     5 
     6         public ProductService(IProductRepository productRepository, ICacheStorage cacheStorage)
     7         {           
     8             _productRepository = productRepository;
     9             _cacheStorage = cacheStorage;
    10         }
    11 
    12         public IList<Product> GetAllProductsIn(int categoryId)
    13         {
    14             IList<Product> products;
    15             string storageKey = string.Format("products_in_category_id_{0}", categoryId);
    16 
    17             products = _cacheStorage.Retrieve<List<Product>>(storageKey);
    18 
    19             if (products == null)                
    20             {
    21                 products = _productRepository.GetAllProductsIn(categoryId);
    22                 _cacheStorage.Store(storageKey, products);
    23             }
    24 
    25             return products;
    26         }
    27     }
    ProductService

    4、Null Object 模式

    空对象模式:在一些特殊的情况下用户不希望连带的执行某个动作,也不希望传递null在框架里做判断,就用到空对象模式。这个很容易理解,只是让附带动作变为do nothing 

     1 public class NullObjectCachingAdapter : ICacheStorage 
     2     {        
     3         public void Remove(string key)
     4         {
     5             // Do nothing
     6         }
     7 
     8         public void Store(string key, object data)
     9         {
    10             // Do nothing
    11         }
    12 
    13         public T Retrieve<T>(string storageKey)
    14         {
    15             return default(T);
    16         }        
    17     }
    NullObjectCachingAdapter

     四、小结

    通过依赖注入和依赖倒置,把创建什么样对象的工作交给客户端(Client),降低Service、Repository、Cache之间的耦合度。假如有新的产品,只有实现了IRepositiry接口,在客户端创建对象,注入给service的构造函数就可以了。




    1、如果您觉得阅读本文对您有帮助,请点一下“推荐”按钮,您的“推荐”将是我最大的写作动力!
    2、欢迎各位转载,但是未经作者本人同意,转载文章请在文章页面明显位置标明作者和原文连接,否则保留追究法律责任的权利。
    作者博客: http://www.cnblogs.com/xmai/
  • 相关阅读:
    Gstreamer 随笔
    Git 提交(commit)没有自动生成Change-Id导致无法push
    xmake构建C/C++编译环境
    编译configure常用参数详解
    ubuntu上安装meson & 如何使用meson编译C代码
    如何使用autotools/automake自动生成Makefile文件
    如何扩大镜像文件容量
    Mac OS X 键盘快捷键
    django 文件目录结构
    oh-my-zsh 更换主题后特殊字符显示错误
  • 原文地址:https://www.cnblogs.com/xmai/p/Patterns_L01.html
Copyright © 2020-2023  润新知