设计模式—享元模式
前言
在软件开发过程中,当我们需要重复使用某个对象时就需要‘new’这个关键字重复去创造,这样就需要多次去申请内存空间,可能会出现内存越来越多的情况,这样的问题是很严重的。看看下面的享元模式是怎么解决的。
享元模式介绍
重复创建对象的问题,可以这样解决:既然是同一个对象,能不能只创建一个对象,下次需要创建这个对象的时候直接用已经创建好的对象就好了,即是让一个对象共享
1)定义
享元模式--运用共享技术有限的支持大量细粒度的对象。享元模式可以避免大量相似类的开销,如果很多实例除了几个参数外基本上都是相同的,这时候就可以使用享元模式来减少需要实例化类的数量,如果把这些参数移到类实例外面,在方法调用时将他们传递进了,这样可以通过共享大幅度的减少单个实例的数目。办类实例外的参数称为享元模式的外部状态,把在享元对象内部的参数定义为内部状态。内部状态:在享元对象的内部并且不会随着环节的改变而改变的共享部分
外部状态:随环境改变而改变,不可能共享的状态
2)实际例子
比如说象棋,象棋都是圆的,这属于公共部分称之为内部状态,但是象棋上面的子是不一样的,这就属于是外部状态
1 using System; 2 using System.Collections; 3 using System.Collections.Generic; 4 using System.Linq; 5 using System.Text; 6 7 namespace ConsoleApplication1 8 { 9 //享元工厂,负责创建和管理享元对象 10 public class ChessFlyweightFactory 11 { 12 public Hashtable flyweights = new Hashtable(); 13 14 public ChessFlyweightFactory() 15 { 16 flyweights.Add("圆型", GetFlyweight("圆形")); 17 } 18 19 //返回实际对象,如果对象之前创建过直接返回,如果没有创建过则创建新对象并保存起来,以供下次使用 20 public ChessFlyweight GetFlyweight(string key) 21 { 22 ChessFlyweight chess = flyweights[key] as ChessFlyweight; 23 if (chess == null) 24 { 25 var newFlyweight = new ConcreteChessFlyweight(key); 26 flyweights.Add(key, newFlyweight); 27 return newFlyweight; 28 } 29 return chess; 30 } 31 } 32 33 //抽象享元类,提供享元类具有的方法 34 public abstract class ChessFlyweight 35 { 36 public abstract void Operation(string extrinsicstate); 37 } 38 39 //具体享元对象 40 public class ConcreteChessFlyweight : ChessFlyweight 41 { 42 //内部状态 43 private string intrinsicstate; 44 45 //构造方法,传递参数称之为外部状态 46 public ConcreteChessFlyweight(string intrinsicstate) 47 { 48 this.intrinsicstate = intrinsicstate; 49 } 50 51 public override void Operation(string extrinsicstate) 52 { 53 Console.WriteLine(string.Format("具体实现类:内部状态{0},外部状态{1}", intrinsicstate, extrinsicstate)); 54 } 55 } 56 }
调用方法:
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 6 namespace ConsoleApplication1 7 { 8 class Program 9 { 10 //4>:客户端(Client)角色:需要维护一个对所有享元对象的引用;需要自行存储所有享元对象外蕴状态。 11 static void Main(string[] args) 12 { 13 //初始化享元工厂 14 ChessFlyweightFactory factory = new ChessFlyweightFactory(); 15 //创建圆形象棋 16 ChessFlyweight chess = factory.GetFlyweight("圆形"); 17 18 //定义外部状态,象棋上的字 19 string externalstate1 = "炮"; 20 chess.Operation(externalstate1); 21 22 string externalstate2 = "将"; 23 chess.Operation(externalstate2); 24 } 25 } 26 }
运行结果:
3)享元模式介绍
从享元模式的类图中可以看出有以下几个角色:
抽象享元角色:所有具体享元类的基类,为这些类规定需要实现的接口,那些需要外部状态的操作可以通过调用方法一参数形式传入
具体享元角色:实现抽象享元角色所规定的接口,如果有内部状态的话可以定义内部状态
享元角色工厂:负责创建和管理享元角色,本角色必须保证享元对象可以被系统共有。当一个客户端对象调用一个享元对象的时候,享元工厂角色检查系统中是否已经有一个符合要求的享元对象,如果已经存在,享元工厂角色就提供已存在的享元对象,如果系统中没有一个符合的享元对象的话,享元工厂角色就应当创建一个合适的享元对象。
客户端角色:本角色需要存储所有享元对象的外部状态
5)享元模式分析
优点:降低了系统中对象数量,从而降低对内存造成的压力
缺点:使系统的逻辑复杂化,同时享元模式将享元对象的状态外部化,读取时使得运行时间稍微变长
6)享元模式使用场景
一个系统中有大量的对象;
这些对象耗费大量的内存;
这些对象中的状态大部分都可以被外部化;
这些对象可以按照内部状态分成很多的组,当把外部对象从对象中剔除时,每一个组都可以仅用一个对象代替;
软件系统不依赖这些对象的身份;
但是使用享元模式需要额外维护一个记录子系统已有的所有享元的表,而这也需要耗费资源,所以,应当在有足够多的享元实例可共享时才值得使用享元模式。