设计模式-借还模式

我个人比较注重代码的性能,包括时间和空间,代码的可控度;在做开发的时候偶然发现并实现了这个方式,我个人命名他为“借还模式”。不知道是否有相关的模式,我读书少,嘿嘿。。。

很多时候,我们执行一个方法,必须要有很多的new操作,当这个方法执行次数非常多的时候,这个方法内部new的操作将是超级多的。但是我们又不能手动释放内存,只能等待GC来回收。那么我们就有一个问题,既然不能手动释放,而且反正要new,那么干脆我们就不让GC去回收了,让他持续的存在,这样还能节省内存的空间,不是一举两得吗?由此我想到图书馆的图书实现过程,图书的所有权是图书馆的,借出去的是使用权,用完了必须归还。那么就有两个关键的操作“借”和“还”,“借”和“还”都指的是使用权。

代码实现如下:

  1     /// <summary> 对象借还管理器 </summary>
  2     public class BRManage : IDisposable
  3     {
  4         private static BRManage br;
  5         private static readonly object ins = new object();
  6         private readonly object globalLock = new object();
  7
  8         private const int WARNING_COUNT = 10;
  9         private readonly Dictionary<Type, Queue<object>> cachePool = new Dictionary<Type, Queue<object>>();
 10         private readonly Dictionary<Type, List<object>> cacheBak = new Dictionary<Type, List<object>>();
 11
 12         private bool isdisposing;
 13
 14         private BRManage()
 15         {
 16         }
 17
 18         /// <summary> 单例 </summary>
 19         public static BRManage Ins
 20         {
 21             get
 22             {
 23                 if (br == null)
 24                 {
 25                     lock (ins)
 26                     {
 27                         if (br == null)
 28                         {
 29                             br = new BRManage();
 30                         }
 31                     }
 32                 }
 33
 34                 return br;
 35             }
 36         }
 37
 38         ///<summary> 借出一个对象 </summary>
 39         public T Borrow<T>() where T : class, new()
 40         {
 41             if (isdisposing)
 42                 throw new ApplicationException("借还管理器已经释放,不能再使用,请重启系统");
 43
 44             var type = typeof (T);
 45
 46             if (!cacheBak.ContainsKey(type))
 47             {
 48                 lock (globalLock)
 49                 {
 50                     if (!cacheBak.ContainsKey(type))
 51                     {
 52                         cachePool.Add(type, new Queue<object>());
 53                         cacheBak.Add(type, new List<object>());
 54                     }
 55                 }
 56             }
 57
 58             var que = cachePool[type];
 59             var list = cacheBak[type];
 60
 61             var bak = WARNING_COUNT - que.Count;
 62             if (bak > 0)
 63             {
 64                 for (int i = 0; i < bak; i++)
 65                 {
 66                     var temp = new T ();
 67                     list.Add(temp);
 68                     que.Enqueue(temp);
 69                 }
 70             }
 71
 72             return que.Dequeue() as T;
 73         }
 74
 75         ///<summary> 返还一个对象 </summary>
 76         public void Return<T>(T obj) where T : class
 77         {
 78             if (isdisposing)
 79                 throw new ApplicationException("借还管理器已经释放,不能再使用,请重启系统");
 80
 81             if (obj == null)
 82                 throw new ArgumentNullException("obj");
 83
 84             var type = obj.GetType();
 85             if (!cachePool.ContainsKey(type))
 86             {
 87                 throw new ArgumentNullException("obj", "该类型未注册过");
 88             }
 89
 90             if (cacheBak[type].IndexOf(obj) == -1)
 91             {
 92                 throw new ArgumentException("该对象未被索引,不被借还管理", "obj");
 93             }
 94
 95             cachePool[type].Enqueue(obj);
 96         }
 97
 98         /// <summary> 释放所有数据 </summary>
 99         public void Dispose()
100         {
101             isdisposing = true;
102
103             var list = new List<Type>(cachePool.Keys);
104             for (int i = 0, l = list.Count; i < l; i++)
105             {
106                 cachePool[list[i]].Clear();
107                 cachePool[list[i]].TrimExcess();
108             }
109             cachePool.Clear();
110             list.Clear();
111
112             list = new List<Type>(cacheBak.Keys);
113             for (int i = 0, l = list.Count; i < l; i++)
114             {
115                 cacheBak[list[i]].Clear();
116                 cacheBak[list[i]].TrimExcess();
117             }
118             cacheBak.Clear();
119             list.Clear();
120             list.TrimExcess();
121         }
122     }

代码解释:

这里单例是为了方便操作,我不喜欢new,也不喜欢static。new代表着空间减少,static如果单纯的作为计算数据没问题,如果保存数据带来并发问题,避免这些问题要增加复杂度。cacheBak的作用是保存对象的所有权,作用是在Dispose的时候释放列表。当然Dispose这里比较简单,在实际用的时候可能需要调用对象的Close或者Dispose方法。cachePool是一个队列,队列是个好东西,这里用来记录对象的使用权,表示还有那些对象可供借出。这里借出对象是没有选择性的,实际根据业务可以有选择的借出。另外有一个WARNING_COUNT常量,相当于警告库存的概念,让使用权队列始终大于10个长度,这样避免并发操作Dequeue抛出操作异常的问题,当然是一定程度上。

借还模式注意事项:

  1. Borrow和Return必须成对出现,正所谓“有借有还再借不难”,否则就是人品问题;那么代码就是质量问题,如果不Return的后果可能是内存占用量急剧增长。
  2. 这里用了泛型方法和泛型约束,要求T必须是class,很显然如果是值类型,按副本传递就失去了意义;并且可以new,因为在Borrow里面需要创建对象。
  3. 在使用完毕的时候,一般系统或者程序关闭的时候执行Dispose,释放使用权和所有权。
  4. 如果需要的话,可以在Return或者Dispose的时候可以对对象进行一些操作。
  5. 对于非托管代码慎用,例如对同一个文件操作的Stream可能会出现不可以意料的异常。

借还模式的扩展:

  1. 可以增加限制让cacheBak所有权列表的长度,不要让所有权列表一直增长下去,可以限制在某个界限值,如果没有可用使用权对象,线程就Sleep一会。
  2. 可以对Type类型做限制,增加安全性,可以增加一个Register方法,对能使用的Type进行注册,而不是在Borrow中自动创建
  3. 如果Return的对象在其他地方还有保留的引用,而我又不想让这些保留的引用使用的时候,可以在Return的时候对这个对象进行一些的操作,防止其他保留的引用继续使用,当然比较麻烦。

使用场景:

  1. 大量小对象的创建,可以节省内存
  2. 珍惜资源的创建,嘿嘿,你是否想到了不关闭的Connection对象,我这么用了,目前没问题。

测试结果:

先看测试代码如下

class Program
    {
        static void Main(string[] args)
        {
            var dt = DateTime.Now;
            // A 方式
            //for (int i = 0; i < 10000000; i++)
            //{
            //    var o = new MyClass();
            //    o.p1 = 123;
            //    o.p2 = o.p1;
            //    o.p3 = 12;
            //    o.p4 = o.p1.ToString();
            //    o.p5 = Guid.NewGuid();
            //}
            //Console.WriteLine((DateTime.Now-dt).TotalMilliseconds);

            // B 方式
            dt = DateTime.Now;
            for (int i = 0; i < 10000000; i++)
            {
                var o = BRManage.Ins.Borrow<MyClass>();
                BRManage.Ins.Return(o);
            }
            Console.WriteLine((DateTime.Now - dt).TotalMilliseconds);
            Console.ReadLine();
        }
    }

    class MyClass
    {
        public int p1 { get; set; }
        public int p2 { get; set; }
        public byte p3 { get; set; }
        public string p4 { get; set; }
        public Guid p5 { get; set; }
    }
  CPU极值 执行时间(ms)  最高内存占用(m)
A方案 35% 4300-4500 3.7M
B方案 35% 4300-4500 2.1M

测试比较片面,实际应用的时候比较复杂,整个系统测试比较麻烦,个人认为总的占用空间会减少,同时性能不会降低。

综述:该模式依然是创建模式,主要功能是能节省一定的空间,例子代码中的性能和直接new的性能是一样的,测试的时候有上下浮动,如果对Borrow和Return进行其他操作的时候多多少少会影响一些性能,大家就自己斟酌吧。欢迎园友拍砖,丢鸡蛋。

时间: 2024-10-05 11:39:20

设计模式-借还模式的相关文章

设计模式之单列模式

设计模式之单列模式 1,何为单列模式? 即singleton 在某个类采用了单列模式之后  其只能有一个实列对象 ,并且这个实列对象只能有内部自己创建并提供给外部的调用. 2.实现单列模式的方法 分为 :饿汉式 ,懒汉式 下面为饿汉式实现代码: public calss Singleton1{ //将构造函数私有化 防止外部通过new来创建对象 private Singleton1(){ } //创建一个私有静态变量并直接初始化 类加载的时候直接创建对象 private static Singl

每天一个设计模式-7 生成器模式(Builder)

每天一个设计模式-7 生成器模式(Builder) 一.实际问题 在讨论工厂方法模式的时候,提到了一个导出数据的应用框架,但是并没有涉及到导出数据的具体实现,这次通过生成器模式来简单实现导出成文本,Xml等具体的格式. 导出成文本或Xml等格式的数据时,一般都会有各自的格式,比如:导出的文件都有3个部分,文件头,内容,尾. 二.问题分析 无论哪种导出格式,都需要3个部分,文件头,内容,尾等信息,并且他们的内容相同.即他们的构造算法固定,只是生成的结果不同:能不能把算法(构建)和结果(外观)分离出

Android设计模式之代理模式 Proxy

一.概述 代理模式也是平时比较常用的设计模式之一,代理模式其实就是提供了一个新的对象,实现了对真实对象的操作,或成为真实对象的替身.在日常生活中也是很常见的.例如A要租房,为了省麻烦A会去找中介,中介会替代A去筛选房子,A坐享中介筛选的结果,并且交房租也是交给中介,这就是一个典型的日常生活中代理模式的应用.平时打开网页,最先开到的一般都是文字,而图片等一些大的资源都会延迟加载,这里也是使用了代理模式. 代理模式的组成: Abstract Subject:抽象主题-声明真实主题和代理主题共同的接口

设计模式之备忘录模式(Memento)摘录

23种GOF设计模式一般分为三大类:创建型模式.结构型模式.行为模式. 创建型模式抽象了实例化过程,它们帮助一个系统独立于如何创建.组合和表示它的那些对象.一个类创建型模式使用继承改变被实例化的类,而一个对象创建型模式将实例化委托给另一个对象.创建型模式有两个不断出现的主旋律.第一,它们都将关于该系统使用哪些具体的类的信息封装起来.第二,它们隐藏了这些类的实例是如何被创建和放在一起的.整个系统关于这些对象所知道的是由抽象类所定义的接口.因此,创建型模式在什么被创建,谁创建它,它是怎样被创建的,以

设计模式之策略模式(Strategy)摘录

23种GOF设计模式一般分为三大类:创建型模式.结构型模式.行为模式. 创建型模式抽象了实例化过程,它们帮助一个系统独立于如何创建.组合和表示它的那些对象.一个类创建型模式使用继承改变被实例化的类,而一个对象创建型模式将实例化委托给另一个对象.创建型模式有两个不断出现的主旋律.第一,它们都将关于该系统使用哪些具体的类的信息封装起来.第二,它们隐藏了这些类的实例是如何被创建和放在一起的.整个系统关于这些对象所知道的是由抽象类所定义的接口.因此,创建型模式在什么被创建,谁创建它,它是怎样被创建的,以

设计模式之代理模式(Proxy)摘录

23种GOF设计模式一般分为三大类:创建型模式.结构型模式.行为模式. 创建型模式抽象了实例化过程,它们帮助一个系统独立于如何创建.组合和表示它的那些对象.一个类创建型模式使用继承改变被实例化的类,而一个对象创建型模式将实例化委托给另一个对象.创建型模式有两个不断出现的主旋律.第一,它们都将关于该系统使用哪些具体的类的信息封装起来.第二,它们隐藏了这些类的实例是如何被创建和放在一起的.整个系统关于这些对象所知道的是由抽象类所定义的接口.因此,创建型模式在什么被创建,谁创建它,它是怎样被创建的,以

[设计模式] Typed Message模式与Event Sourcing

引言 在<设计模式沉思录>(Pattern Hatching: Design Patterns Applied,[美]JohnVlissides著)一书的第4章中,围绕事件Message传递的推-拉模型(Push-Pull),谈到了一个最初被称为Multicast,之后被定型为Typed Message的设计模式. 该模式的初衷是希望得到一个可扩展的.类型安全的事件传递机制,并能符合两点要求: 通过推模型来将事件传递给消费者. 特有的每个事件只需要最多新增一个类,而不需要对已有代码进行修改.

C++设计模式之建造者模式(二)

3.省略指挥者Director的建造者模式 指挥者类Director在建造者模式中扮演非常重要的作用,简单的Director类用于指导具体建造者如何构建产品,它按一定次序调用Builder的buildPartX()方法,控制调用的先后次序,并向客户端返回一个完整的产品对象.Direcotr针对抽象的建造者进行编程,如果需要不同的建造者,只需把建造者传入指挥者类,无需修改之前的代码. 在有些情况下,为了简化系统结构,可以省略指挥者Director,指挥者不再指导产品的创建过程.而是在Builder

设计模式之组合模式(Composite)摘录

23种GOF设计模式一般分为三大类:创建型模式.结构型模式.行为模式. 创建型模式抽象了实例化过程,它们帮助一个系统独立于如何创建.组合和表示它的那些对象.一个类创建型模式使用继承改变被实例化的类,而一个对象创建型模式将实例化委托给另一个对象.创建型模式有两个不断出现的主旋律.第一,它们都将关于该系统使用哪些具体的类的信息封装起来.第二,它们隐藏了这些类的实例是如何被创建和放在一起的.整个系统关于这些对象所知道的是由抽象类所定义的接口.因此,创建型模式在什么被创建,谁创建它,它是怎样被创建的,以