65.9K
CodeProject 正在变化。 阅读更多。
Home

享元设计模式

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.60/5 (4投票s)

2015年8月11日

CPOL

4分钟阅读

viewsIcon

19414

downloadIcon

180

享元模式是关于创建一个对象池,允许共享已经创建的对象,并导致应用程序消耗更少的内存。

享元设计模式是 GOF 引入的结构型模式之一。享元模式是关于创建一个对象池,允许共享已经创建的对象,并导致应用程序消耗更少的内存。

所以这个模式做了两件事,因为它只创建一次对象,然后将它保存到池中

  1. 在对象创建方面提高应用程序性能,因为不需要在每个请求都创建一个对象。
  2. 由于创建的对象已经在内存中,并且没有新的对象池,因此导致应用程序消耗更少的内存。

最终,享元模式使应用程序在内存和处理方面都变得高效。

设计模式的基本 UML 类图

下图显示了基本享元设计模式的类图

IFlyweight - 派生类型(即具体享元)需要实现的基本契约。

FlyweightFactory – 这是一个工厂类,客户端类使用它来从具体享元获取数据。此类还负责创建已创建对象的池。因此,当请求的数据已经被请求过时,它会从池中返回数据。

ConcerateSharedFlyweight1 – 这是享元接口的具体实现。顾名思义,此类返回的对象将在其客户端之间共享。

ConcerateUnSharedFlyweight1 – 这是享元接口的具体实现。顾名思义,此类返回的对象将是非共享的,这意味着每次客户端都需要一个新对象。

客户端 – 使用具体实现,它创建 Decorate 的一个实例并使用它的一个功能。

注意

UnSharedFlyweight 并非总是必需的,这取决于需求,但是当您使用享元模式时,总是需要 SharedFlyweight

下面的代码是享元设计模式的实现以及上面讨论的类图。

namespace BasicFlyweightPattern
{
    #region basic implementation
    public class FlyweightFactory
    {
        public static List<Item> GetStaticItemList(string key)
        {
            IFlyWeight flyWeight = null;
            ICacheManager _objCacheManager = CacheFactory.GetCacheManager();
 
            if (key == "A")
            {
                if (_objCacheManager.Contains("A"))
                {
                    return _objCacheManager["A"] as List<Item>;
                }
                else
                {
                    flyWeight = new ConcerateSharedFlyweight1();
                }
            }
            else if (key == "B")
            {
                if (_objCacheManager.Contains("B"))
                {
                    return _objCacheManager["B"] as List<Item>;
                }
                else
                {
                    flyWeight = new ConcerateSharedFlyweight2();
                }
            }
 
            var list = flyWeight.GetList();
            _objCacheManager.Add(key, list);
            return list;
        }
    }
 
    interface IFlyWeight
    {
        List<Item> GetList();
    }
 
    public class Item
    {
        public int id { get; set; }
        public string desc { get; set; }
    }
 
    public class ConcerateSharedFlyweight1 : IFlyWeight
    {
        private List<Item> ItemList;
 
        public ConcerateSharedFlyweight1()
        {
            ItemList = new List<Item>();
        }
 
        public List<Item> GetList()
        {
            ItemList.Add(new Item { id = 1, desc = "A1" });
            ItemList.Add(new Item { id = 2, desc = "A2" });
            ItemList.Add(new Item { id = 3, desc = "A3" });
            return ItemList;
        }
    }
 
    public class ConcerateSharedFlyweight2 : IFlyWeight
    {
        private List<Item> ItemList;
 
        public ConcerateSharedFlyweight2()
        {
            ItemList = new List<Item>();
        }
 
        public List<Item> GetList()
        {
            ItemList.Add(new Item { id = 1, desc = "B1" });
            ItemList.Add(new Item { id = 2, desc = "B2" });
            ItemList.Add(new Item { id = 3, desc = "B3" });
            return ItemList;
        }
    }
 
    class Program
    {
        static void Main(string[] args)
        {
            List<Item> list =FlyweightFactory.GetStaticItemList("A");
     //List<Item> list = FlyweightFactory.GetStaticItemList("B");           
     foreach (var item in list)
            {
                Console.WriteLine(item.id.ToString() + "  " + item.desc );
           }
 
            Console.ReadLine();
        }
    }
 
}

在上面的代码实现中需要记住的点

  1. 客户端通过调用 GetStaticItemList 并传递键作为参数来从 FlyweightFactory 请求数据,以获取数据。
  2. FlyweightFactory 具有静态方法 GetStaticItemList,如果请求是第一次,它会创建具体实例并获取数据,对于以后的请求,它会返回现有的创建对象。
  3. FlyweightFactory 通过使用 Enterprise library 来维护已创建对象的池。缓存块用于在不同的客户端(即不同的对象)之间共享数据。
  4. 在享元的具体实现中,返回的是一个硬编码的列表,但在实际场景中,这将替换为从数据源获取数据。
  5. Item 是一个类,它是一个 poco 类,该列表从享元具体类返回。此类由不同的项目表示。以下场景提供了更多详细信息。

输出

这里的输出显示了 A 的结果,B 的结果也是如此。如果第二次调用,则数据从缓存中返回。

真实应用程序中的享元设计模式示例

问题陈述

Web 应用程序具有用于显示国家/地区列表、显示州列表、显示产品列表等的下拉列表,并且这些下拉列表是多个屏幕的一部分,多个用户可以访问这些屏幕。

在这种情况下,为了为多个用户请求显示不同类型的列表,服务器需要连接到数据库服务器多个项目,这会降低应用程序的性能,还会消耗内存来创建和存储这些列表。

解决方案

上述问题的解决方案是使用享元设计模式。

以下是在应用程序中使用的享元模式的类图。

与基本实现的映射

IFlyweightManager 等于 IFlyweight

StaticDataListFlyweightFactory 等于 FlyweightFactory

CountryStaticListManager & ProductStaticListManager 等于 ConcerateSharedFlyweight1 & ConcerateSharedFlyweight2

StaticItem 等于 Item。

namespace FlyWeightPattern
{
    class Program
    {
        static void Main(string[] args)
        {

            List<StaticItem> countrylist = StaticDataListFlyWeidhtFactory.GetStaticItemList("Country");
            foreach (var item in countrylist)
            {
                Console.WriteLine(item.id.ToString() + "  " + item.Code + "  " + item.Description);
            }

            Console.ReadLine();
        }
    }
 public class StaticDataListFlyWeidhtFactory
    {
        public static List<StaticItem> GetStaticItemList(string key)
        {
            IFlyWeightManager manager = null;
            ICacheManager _objCacheManager = CacheFactory.GetCacheManager();

            if (key == "Country")
            {
                if (_objCacheManager.Contains("Country"))
                {
                    return _objCacheManager["Country"] as List<StaticItem>;
                }
                else
                {
                    manager = new CountryStaticListManager();
                }
            }
            else if (key == "ProductType")
            {
                if (_objCacheManager.Contains("ProductType"))
                {
                    return _objCacheManager["ProductType"] as List<StaticItem>;
                }
                else
                {
                    manager = new ProductTypeStaticListManager();
                }
            }

            var list = manager.GetList();
            _objCacheManager.Add(key, list);
            return list;
        }
    }

    interface IFlyWeightManager
    {
        List<StaticItem> GetList();
    }

    public class CountryStaticListManager : IFlyWeightManager
    {
        private List<StaticItem> StaticItemList;

        public CountryStaticListManager()
        {
            StaticItemList = new List<StaticItem>();
        }

        public List<StaticItem> GetList()
        {
            StaticItemList.Add(new StaticItem { id = 1, Code = "IND", Description = "India" });
            StaticItemList.Add(new StaticItem { id = 2, Code = "SRL", Description = "Sri Lanka" });
            StaticItemList.Add(new StaticItem { id = 3, Code = "SA", Description = "South Africa" });
            return StaticItemList;
        }
    }

    public class ProductTypeStaticListManager : IFlyWeightManager
    {
        private List<StaticItem> StaticItemList;

        public ProductTypeStaticListManager()
        {
            StaticItemList = new List<StaticItem>();
        }

        public List<StaticItem> GetList()
        {
            StaticItemList.Add(new StaticItem { id = 1, Code = "0123", Description = "Watch" });
            StaticItemList.Add(new StaticItem { id = 2, Code = "0234", Description = "Shoes" });
            StaticItemList.Add(new StaticItem { id = 3, Code = "0345", Description = "" });
            return StaticItemList;
        }
    }

    public class StaticItem
    {
        public int id { get; set; }
        public string Code { get; set; }
        public string Description { get; set; }
    }
}

输出

因此,上面的代码的工作方式与模式的基本实现中已经描述的相同。在代码中,通过传递不同的键来获取不同类型的列表。对于此实现,键是 Country 和 Product。在实际应用中,可能不止这些。

上面的实现遵循 SOLID 原则。一个例外是 Factory 类,当想要添加一个新键时需要修改它,因此它违反了单一职责的规则。

注意

在这里,国家/地区和产品列表是硬编码的,但实际上它是从数据库中获取的。

结论

当想要创建一个对象池并在不同的客户端(客户端是软件程序或类或 Web 应用程序,如本例所示)之间共享它时,此模式非常有用。

注意

这是我对模式的看法。如果您发现任何错误,请提供您的反馈。

© . All rights reserved.