c#享元模式详解
基本介绍:
享元模式的定义:运用共享技术有效地支持大量细粒度的对象重复使用。适用于大量小粒度的对象造成的运行效率和内存使用效率低下的情况。
“享元”顾名思义,“享”共享的意思,“元”单元,最小对象,零部件的意思。
即从字面意思不难看出,该模式旨在共享一些零部件供其使用。
想要实现对这些零部件的重复使用,那必然需要一个类来统筹安排,负责它们的创建、使用和维护。
如果想要实现一个零部件的使用,可以使用单例模式,所以享元模式其实也可以看做是单例模式的复数模式。
它们都是在一个固定类中对对象进行创建和维护。
举例说明:
比如五子棋游戏,构成游戏的组件,无非就是无数个黑棋和白棋。
黑棋和白棋就可以看做是两个最小的单元,在对战过程中就是在重复的创建这些单元。
如果是一般模式一盘棋局必然要创建N多个黑棋和白棋的对象,如果是一盘游戏还可以凑合使用。
大家想象一下,如果是个游戏平台,可以同时开展1000盘这样的棋局,那必然需要创建N*1000个黑白棋子对象。
其实这些对象都是重复的,只有很少一部分属性(状态)不同而已,相同的是棋子本身,不同的是棋子的颜色,比如黑棋和白棋之分。
另外该棋子在哪个棋盘、在哪个棋盘坐标就属于不可被共享的部分了,这部分内容就是非共享的。
既然弄清楚了相同的部分和不同部分,我们就可以把相同的部分进行共享,对象个数从原来的N*1000个对象降到了2个对象。
基本结构:
通过例子也不难看出,享元模式创建的对象存在两个状态:
内部状态:可以被共享的状态。存储在享元信息内部,并且不会随环境的改变而改变。在这里指的是棋子本身,它们不会随着棋局和选手的变化而变化。
外部状态:不可被共享的状态。随环境的改变而改变。在这里指的是黑白棋子颜色之分。
这里容易让人产生误区,容易把棋子所处坐标和棋盘等归在外部状态,这就大错特错了。
能共享的都是最基本的单元,而棋子的坐标和棋盘是不断变化的,随着棋局的不同而不同,这部分是不可以被共享的,也不能被共享。
享元模式的主要有以下角色:
抽象享元角色(Flyweight):通常是一个接口或抽象类,声明了具体享元类公共的方法,这些方法可以为外界提供内部状态和设置外部状态。
具体享元角色(Concrete Flyweight):实现了抽象享元类,在该类中为内部状态提供了存储空间。通常我们可以结合单例模式来设计具体享元类,为每一个具体享元类提供唯一的享元对象。
非享元角色 (Unsharable Flyweight):并不是所有的抽象享元类的子类都需要被共享,不能被共享的子类可设计为非共享具体享元类;当需要一个非共享具体享元类的对象时可以直接通过实例化创建。本实例中,棋盘类就是非享元部分。
享元工厂角色(Flyweight Factory):负责创建和管理享元角色。当客户对象请求一个享元对象时,享元工厂检査系统中是否存在符合要求的享元对象,如果存在则提供给客户;如果不存在的话,则创建一个新的享元对象。
优缺点:
优点:降低了系统中对象的数量,从而降低了系统中细粒度对象给内存带来的压力。享元模式中的外部状态相对独立,且不影响内部状态。
缺点:为了使对象可以共享,需要将享元对象的部分状态外部化,分离内部状态和外部状态,这使得程序的逻辑更复杂,使系统复杂化。
具体实例:
抽象享元角色
1 /// <summary>
2 /// 享元抽象类
3 /// </summary>
4 public abstract class ChessPieces
5 {
6 public string Colour;
7 public ChessPieces(string strColour)
8 {
9 Colour = strColour;
10 }
11
12
13 /// <summary>
14 /// 落子规则
15 /// </summary>
16 /// <param name="chessBoard">棋盘信息</param>
17 public void MoveInChess(ChessBoard chessBoard)
18 {
19 //这里可以将棋盘信息通过参数方式注入
20 }
21
22 //还可以设置一些 棋子的规则等等
23 }享元抽象类主要是规范具体享元类供其继承,并提供其共有的属性和方法。
本实例只是最简单的使用构造函数参数注入方式,将棋子外部状态(颜色)更新。
而内部状态,比如棋子的具体下棋规则等等,可以在此类中声明。
比如此实例MoveInChess方法就是通过参数注入的形式将棋盘信息注入到内容,从而落实落子的具体规则。
规则是通用的,可以共享的,所以可以是内部状态的一部分。
- 具体享元角色
1 /// <summary>
2 /// 白棋
3 /// </summary>
4 public class BlackPieces : ChessPieces
5 {
6 public BlackPieces(string strColour) : base(strColour)
7 {
8
9 }
10 }
11
12 /// <summary>
13 /// 黑棋
14 /// </summary>
15 public class WhitePieces : ChessPieces
16 {
17 public WhitePieces(string strColour) : base(strColour)
18 {
19
20 }
21 }继承自抽象享元类,具体实现其方法。
此实例只是通过构造函数简单的将棋子分成黑白棋子,实际中会有各种属性或方法需要在此类中实现。
- 享元工厂角色
/// <summary>
/// 棋子工厂
/// </summary>
public class WuziqiFactory
{
// 单例模式工厂
private static WuziqiFactory wuziqiFactory;
// 缓存存放共享对象
private static Dictionary<string, ChessPieces> keyValuePairs = new Dictionary<string, ChessPieces>();
// 私有化构造方法
private WuziqiFactory()
{
if (keyValuePairs.Count == 0)
{
keyValuePairs.Add("Black", new BlackPieces("Black"));
keyValuePairs.Add("White", new WhitePieces("White"));
}
} // 获得单例工厂
public static WuziqiFactory GetInstance
{
get
{
if (wuziqiFactory == null)
{
wuziqiFactory = new WuziqiFactory();
}
return wuziqiFactory;
}
} // 获取棋子
public ChessPieces GetChessPieces(String type)
{
if (keyValuePairs.ContainsKey(type))
{
return keyValuePairs[type];
}
return null;
}
}此实例使用单例模式创建工厂对象,保证整个项目生命周期内只存在一个棋子工厂对象,并使用GetInstance进行返回具体对象。这里可以加上锁防止并发等问题。
另外工厂的构造函数对可共享对象进行了缓存,使用GetChessPieces获取棋子对象时,可保证其不重复创建。保证整个项目生命周期内只存在黑白两个棋子对象。
- 非享元角色
1 /// <summary>
2 /// 棋盘
3 /// </summary>
4 public class ChessBoard
5 {
6 //棋盘编号
7 public int ChessBoardId { get; set; }
8
9
10 //黑方棋手
11 //白方棋手
12 //棋盘棋子布局等等属性
13
14 /// <summary>
15 /// 初始化棋盘
16 /// </summary>
17 public ChessBoard()
18 {
19 //可以通过构造函数 初始化棋盘基础属性
20 }
21 }非享元部分,也就是不能共享的部分。
棋盘的编号、棋盘对局双方信息和棋盘落子情况等都是每个棋盘独有的,不可共享。
至于棋盘的落子和整体维护可以通过参数注入等形式交给棋子共享对象进行研判和操作,或者直接在棋盘类中进行声明都可以,这就看具体制定的规则了。
客户端
1 /// <summary>
2 /// 客户端
3 /// </summary>
4 class Client
5 {
6 static void Main(string[] args)
7 {
8 //创建棋盘 通过构造函数或者函数参数等形式初始化棋手、棋盘信息
9 ChessBoard chessBoard = new ChessBoard();
10 //获取黑方棋子1
11 ChessPieces blackPieces1 = WuziqiFactory.GetInstance.GetChessPieces("Black");
12 Console.WriteLine("棋子:" + blackPieces1.Colour);
13 //获取白方棋子1
14 ChessPieces whitePieces1 = WuziqiFactory.GetInstance.GetChessPieces("White");
15 Console.WriteLine("棋子:" + whitePieces1.Colour);
16
17 //判断两个棋子是否是同一个对象
18 Console.WriteLine("判断两个不同颜色的棋子是否是同一个对象" + blackPieces1.Equals(whitePieces1) + "\r\n");
19
20 //获取黑方棋子2
21 ChessPieces blackPieces2 = WuziqiFactory.GetInstance.GetChessPieces("Black");
22 Console.WriteLine("棋子:" + blackPieces2.Colour);
23 //获取白方棋子2
24 ChessPieces whitePieces2 = WuziqiFactory.GetInstance.GetChessPieces("White");
25 Console.WriteLine("棋子:" + whitePieces2.Colour);
26
27 //判断同一个颜色的两个棋子是否是同一个对象
28 Console.WriteLine("判断两个不同颜色的棋子是否是同一个对象" + blackPieces1.Equals(blackPieces2) + "\r\n");
29
30 Console.ReadKey();
31 }
32 }
总结:
实现享元工厂类时使用单例模式和简单工厂模式,确保对象的唯一性,并提供方法向客户端返回享元对象。
c#享元模式详解的更多相关文章
- 【享元设计模式详解】C/Java/JS/Go/Python/TS不同语言实现
简介 享元模式(Flyweight Pattern),是一种结构型设计模式.主要用于减少创建对象的数量,以减少内存占用和提高性能.它摒弃了在每个对象中保存所有数据的方式,通过共享多个对象所共有的相同状 ...
- C#设计模式:享元模式(Flyweight Pattern)
一,什么是享元模式? 享元模式(Flyweight Pattern):采用共享技术来避免大量拥有相同内容对象的开销,主要用于减少创建对象的数量,以减少内存占用和提高性能 1,根本的思路就是对象的重用2 ...
- java开发中的23中设计模式详解--大话设计模式
设计模式(Design Patterns) ——可复用面向对象软件的基础 设计模式(Design pattern)是一套被反复使用.多数人知晓的.经过分类编目的.代码设计经验的总结.使用设计模式是为了 ...
- java23中设计模式详解
设计模式(Design Patterns) ——可复用面向对象软件的基础 设计模式(Design pattern)是一套被反复使用.多数人知晓的.经过分类编目的.代码设计经验的总结.使用设计模式是为了 ...
- 设计模式之享元模式(Flyweight)摘录
23种GOF设计模式一般分为三大类:创建型模式.结构型模式.行为模式. 创建型模式抽象了实例化过程,它们帮助一个系统独立于怎样创建.组合和表示它的那些对象.一个类创建型模式使用继承改变被实例化的类,而 ...
- JAVA 23种开发模式详解(代码举例)
设计模式(Design Patterns) ——可复用面向对象软件的基础 设计模式(Design pattern)是一套被反复使用.多数人知晓的.经过分类编目的.代码设计经验的总结.使用设计模式是为了 ...
- Java开源生鲜电商平台-盈利模式详解(源码可下载)
Java开源生鲜电商平台-盈利模式详解(源码可下载) 该平台提供一个联合买家与卖家的一个平台.(类似淘宝购物,这里指的是食材的购买.) 平台有以下的盈利模式:(类似的平台有美菜网,食材网等) 1. 订 ...
- 重学 Java 设计模式:实战享元模式「基于Redis秒杀,提供活动与库存信息查询场景」
作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言 程序员的上下文是什么? 很多时候一大部分编程开发的人员都只是关注于功能的实现,只 ...
- Flyweight(享元模式)
import java.util.Hashtable; /** * 享元模式 * @author TMAC-J * 享元模式一般和工厂模式一起使用,但此处为了更好说明,只用享元模式 * 定义:享元模式 ...
- 设计模式(十二)享元模式(Flyweight Pattern)
一.引言 在软件开发过程,如果我们需要重复使用某个对象的时候,如果我们重复地使用new创建这个对象的话,这样我们在内存就需要多次地去申请内存空间了,这样可能会出现内存使用越来越多的情况,这样的问题是非 ...
随机推荐
- Kubernetes亲和性学习笔记
欢迎访问我的GitHub 这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos 本篇概览 本文是欣宸在学习Kubernetes调度器的 ...
- Linux基础—vmvare安装及centos7安装
Linux基础 # https://zhuanlan.zhihu.com/p/429509333 # 一 什么是操作系统 Operating System(操作系统) 简称OS Windows,Mac ...
- [nginx]借助nginx实现自动获取本机IP
前言 在用脚本自动化部署应用时,有的应用需要指定本机IP,网上找到的方案大多是过滤ifconfig或者ip命令的结果,这里提供一种通过nginx获取本机ip的方法.大致思路为客户端向nginx发起请求 ...
- React Router@3.x 升级到 @6.x 的实施方案
我们是袋鼠云数栈 UED 团队,致力于打造优秀的一站式数据中台产品.我们始终保持工匠精神,探索前端道路,为社区积累并传播经验价值. 本文作者:景明 升级背景 目前公司产品有关 react 的工具版本普 ...
- [数据分析与可视化] Python绘制数据地图5-MovingPandas绘图实例
MovingPandas是一个基于Python和GeoPandas的开源地理时空数据处理库,用于处理移动物体的轨迹数据.关于MovingPandas的使用见文章:MovingPandas入门指北,本文 ...
- [碎碎念]和ljf老师聊天得到的一些启发,希望大家一起来吹水
关于写这个小文 和ljf老师聊天得到的一些启发,希望能够总结出来方便回顾,并且我觉得这些想法有一定的普适性,可以供大家参考. 疑问 我的疑问是,我现在主要在做fuzz+pwn,能够进行漏洞挖掘,以及w ...
- 几种常用到的 Hybrid App 技术框架
移动操作系统在经历了诸神混战之后,BlackBerry OS.Symbian OS.Windows Phone 等早期的移动操作系统逐渐因失去竞争力而退出.目前,市场上主要只剩下安卓和 iOS 两大阵 ...
- 一次Python本地cache不当使用导致的内存泄露
背景 近期一个大版本上线后,Python编写的api主服务使用内存有较明显上升,服务重启后数小时就会触发机器的90%内存占用告警,分析后发现了本地cache不当使用导致的一个内存泄露问题,这里记录一下 ...
- Maven安装与配置教程
一.安装前检查 检查电脑上是否安装JDK,如果没有安装,请查看JDK安装教程:点我查看 如果电脑上已经安装JDK,按Win 和R键,输入cmd,然后点击确定 输入java -version,点击回车, ...
- Spring中事务的传播行为有哪些?
Spring中事务的传播行为有哪些? 现在我们来谈一个场景,再来引出事务传播行为这个概念.现在有methodA( ) 和 methodB( ),而且两个方法都显示的开启了事务,那么methodB( ) ...