设计模式(0)简单工厂模式

源码地址

0 单例模式简介

0.0 单例模式定义

单例模式是GOF二十三中经典设计模式的简单常用的一种设计模式,单例模式的基本结构需满足以下要求。

  • 单例模式的核心结构只有一个单例类,单例模式要保证这个类在运行期间只能被实例化一次,即只会被创建唯一的一个单例类的实例。
  • 单例模式需要提供一个全局唯一能得到这个类实例的访问点,一般通过定义一个名称类似为GetInstance的公用方法实现这一目的。

要满足上面的两点要求,应该很容易的想到:

1.该类的构造函数应该是私有的,不能随意被实例化是保证只有一个实例的前提。

2.该类需提供一个公开的且返回值类型为单例类类型的公用方法。

来看一下单例模式的基本结构图:

0.1 单例模式应用场景

通过上面对单例模式基本定义的了解,单例模式的应用场景也就很明确了。

单例模式适用于各种系统中某个类的对象只能存在一个类似场景, 我们现在回顾一下上一篇简单工厂模式中的大致实现

/// <summary>/// 简单工厂类/// </summary>public class Factory{/// <summary>/// 创建英雄的静态方法/// </summary>/// <param name="heroName">英雄名称</param>/// <returns></returns>public static IHero CreateHero(string heroName){switch (heroName){case "DH":return new DH();case "WD":return new WD();case "KOG":return new KOG();case "POM":return new POM();default:return null;}}}

/// <summary>/// 恶魔猎手/// </summary>public class DH : IHero{/// <summary>/// 秀出自己的技能/// </summary>public void ShowSkills(){Console.WriteLine("我是恶魔猎手,我会法力燃烧、献祭、闪避和变身。");}}

通过简单工厂模式确实达到了接口隔离的目的,外部使用无需关注内部类的具体实现工程,只通过简单工厂类创建想要的对象即可,但这里有一个致命的问题就是,我们玩儿游戏的过程中,英雄会存在一个死亡和复活的场景,我们简单的把英雄祭坛理解为创建英雄的简单工厂,假设当我们复活英雄的时候,是通过工厂类创建英雄的一个过程,那么我们面临的问题就出现了,我本来一个6级的大恶魔猎手,由于走位过度风骚,走进了祭坛,现在在通过工厂创建的时候,由于是又重新new了一个对象,从祭坛中走出了一个萌叉叉的1级小恶魔猎手……

为保证我的那个6级大恶魔还是那个6级大恶魔,一身装备一个不少的走出祭坛,至此也就到了必须引入单例模式的时候了。

1 单例模式详解

1.0单例模式的基本实现-懒汉式单例模式

按照单例模式的2个基本特征:私有的构造函数公开的GetInstance方法。将DH类进行如下改造,代码的具体意图已经通过注释详细解释。

/// <summary>
/// 恶魔猎手
/// </summary>
public class DH : IHero
{//定义一个静态的DH类变量private static DH dh;/// <summary>/// 私有的构造函数,能够保证该类不会在外部被随意实例化,是保证该类只用一个实例的基本前提/// </summary>private DH(){}/// <summary>/// 定义一个静态的公开的GetInstance方法供外部得到DH类唯一实例是调用/// </summary>/// <returns></returns>public static DH GetInstance(){//先判断dh是否已经被实例化,若未被实例化,先实例化得到DH类的实例//保证DH类只被实例化一次if (dh == null){dh = new DH();}return dh;}/// <summary>/// 秀出自己的技能/// </summary>public void ShowSkills(){Console.WriteLine("我是恶魔猎手,我会法力燃烧、献祭、闪避和变身。");}
}

修改Factory简单工厂类中创建DH实例部分的代码

/// <summary>
/// 简单工厂类
/// </summary>
public class Factory
{/// <summary>/// 创建英雄的静态方法/// </summary>/// <param name="heroName">英雄名称</param>/// <returns></returns>public static IHero CreateHero(string heroName){switch (heroName){case "DH":return DH.GetInstance(); //通过DH类公开的静态GetInstance方法得到DH类的实例case "WD":return new WD();case "KOG":return new KOG();case "POM":return new POM();default:return null;}}
}

客户端测试

static void Main(string[] args)
{IHero dh1 = Factory.CreateHero("DH");IHero dh2 = Factory.CreateHero("DH");if (dh1.Equals(dh2))Console.WriteLine("恶魔猎手:我还是从前的我。");elseConsole.WriteLine("恶魔猎手:我已不是从前的我。");IHero wd1 = Factory.CreateHero("WD");IHero wd2 = Factory.CreateHero("WD");if (wd1.Equals(wd1))Console.WriteLine("守望者:我还是从前的我。");elseConsole.WriteLine("守望者:我已不是从前的我。");Console.ReadLine();
}

输出结果如下

至此我们对DH这个类应用了单例模式来确保无论何时走出祭坛的都是同一个DH对象,从DH对象被实例化的实际来看,是在被使用的时候才会被创建,这种方式被成为懒汉式单例模式

有一天突发奇想,我建造两个英雄祭坛(两个简单工厂类),用我APM500+的超快手速,同时在两个祭坛里生产同一个英雄,发现我拥有了2个6级大恶魔……(当然了,实际中不会有这个bug存在)

这就是基本懒汉式单例模式要面对的多线程问题,也就是说基本懒汉式单例模式的写法是无法做到线程级别安全的

问题的关键就在获取DH类实例的GetInstance方法的内部实现中

if (dh == null)
{dh = new DH();
}
return dh;

简单来说就是当第一个线程调用判断if(dh==null)为true,已经进入内部通过调用new进行实例化时,另一个线程也进行了判断,而恰恰此时dh还没有被实例化完成,同样第二个线程也进入if判断语句的内部,进行dh的实例化,于是就出现了2个DH类的实例,从两个祭坛走出来两个大恶魔。

解决这一问题一般有两种方法饿汉式单例双重检查锁。

1.1 饿汉式单例

饿汉式单例是在系统初始化时自动完成单例类实例的一种方法,而不是等到需要的时候再初始化,也就是说不管以后你会不会用到这个类的对象,我都会给你实例化一个出来,有一种饥饿难耐的感觉在里面,故名饿汉式。

/// <summary>
/// 饿汉式单例
/// </summary>
public class DH : IHero
{//系统初始化时已经将DH类进行实例化private static readonly DH dh = new DH();/// <summary>/// 私有的构造函数,能够保证该类不会在外部被随意实例化,是保证该类只用一个实例的基本前提/// </summary>private DH(){}/// <summary>/// 调用时直接返回已经实例化完成的对象/// </summary>/// <returns></returns>public static DH GetInstance(){return dh;}/// <summary>/// 秀出自己的技能/// </summary>public void ShowSkills(){Console.WriteLine("我是恶魔猎手,我会法力燃烧、献祭、闪避和变身。");}
}

这种方法简单直接的解决了线程安全问题,但是由于实在初始化时就将单例类进行了实例化,一定程度上造成了各种资源的浪费,违背了延迟加载的设计思想,一般为了解决单例模式线程安全问题,通常使用双重检查锁的方法。

1.2 双重检查锁

双重检查锁的命名基于单重检查锁方式而来,单重检查锁是在GetInstance实现的时候先行进行锁定,防止别的线程进入,从而解决线程安全问题的。主要代码如下

//定义一个静态只读的用于加锁的辅助对象
private static readonly object lockObject = new object ();

lock (lockObject)
{//先判断dh是否已经被实例化,若未被实例化,先实例化得到DH类的实例//保证DH类只被实例化一次if (dh == null){dh = new DH();}
}
return dh;

这种方式每次都要进行lock操作,实际上是一种同步方式,这将会在一定程度上影响系统性能的瓶颈和增加了额外的开销。由此衍生出了双重检查锁的方式,简单来说就是先判断一次dh是否为null,为null时才进行lock操作,不为null就直接返回。

/// <summary>
/// 恶魔猎手
/// </summary>
public class DH : IHero
{//定义一个静态的DH类变量private static DH dh;//定义一个静态只读的用于加锁的辅助对象private static readonly object lockObject = new object (); /// <summary>/// 私有的构造函数,能够保证该类不会在外部被随意实例化,是保证该类只用一个实例的基本前提/// </summary>private DH(){}/// <summary>/// 定义一个静态的公开的GetInstance方法供外部得到DH类唯一实例是调用/// </summary>/// <returns></returns>public static DH GetInstance(){//先判断dh是否已经被实例化,若未被实例化,先加锁保证线程安全if (dh == null){lock (lockObject){//先判断dh是否已经被实例化,若未被实例化,先实例化得到DH类的实例//保证DH类只被实例化一次if (dh == null){dh = new DH();}}}return dh;}/// <summary>/// 秀出自己的技能/// </summary>public void ShowSkills(){Console.WriteLine("我是恶魔猎手,我会法力燃烧、献祭、闪避和变身。");}
}

2 总结

本次主要基于上一篇的简单工厂模式,延续的学习使用了单例工厂模式确保一个类实例的全局唯一性,过程中学习了懒汉式、饿汉式、双重检查锁等具体解决方案及演变过程。

设计模式从来不是单打独斗,核心思想是要根据实际需要利用多种模式互相配合来实现代码结构的最优化和健壮性。

转载于:https://www.cnblogs.com/fonour/p/6747430.html

设计模式(1)单例模式(Singleton)相关推荐

  1. 设计模式之单例模式——Singleton

                        设计模式之单例模式--Singleton 设计意图: 保证类仅有一个实例,并且可以供应用程序全局使用.为了保证这一点,就需要这个类自己创建自己的对象,并且对外有 ...

  2. 设计模式之——单例模式(Singleton)的常见应用场景(转):

    单例模式(Singleton)也叫单态模式,是设计模式中最为简单的一种模式,甚至有些模式大师都不称其为模式,称其为一种实现技巧,因为设计模式讲究对象之间的关系的抽象,而单例模式只有自己一个对象,也因此 ...

  3. Java设计模式之单例模式(Singleton Pattern)

    **单例模式:用来创造独一无二的,只能有一个实例的对象设计模式.单例模式确保一个类只有一个实例,并提供一个全局访问点.**相比于全局变量(对对象的静态引用),单例模式可以延迟实例化,而且全局变量不能保 ...

  4. 【设计模式】单例模式 Singleton Pattern

    通常我们在写程序的时候会碰到一个类只允许在整个系统中只存在一个实例(Instance)  的情况, 比如说我们想做一计数器,统计某些接口调用的次数,通常我们的数据库连接也是只期望有一个实例.Windo ...

  5. 设计模式之单例模式(Singleton)摘录

    23种GOF设计模式一般分为三大类:创建型模式.结构型模式.行为模式. 创建型模式包括:1.FactoryMethod(工厂方法模式):2.Abstract Factory(抽象工厂模式):3.Sin ...

  6. Android设计模式——单例模式(Singleton)

    二十三种设计模式分为三大类: 创建型模式,共五种:工厂方法模式.抽象工厂模式.单例模式.建造者模式.原型模式. 结构型模式,共七种:适配器模式.装饰器模式.代理模式.外观模式.桥接模式.组合模式.享元 ...

  7. 设计模式(21):创建型-单例模式(Singleton)

    设计模式(Design pattern)是一套被反复使用.多数人知晓的.经过分类编目的.代码设计经验的总结.使用设计模式是为了可重用代码.让代码更容易被他人理解.保证代码可靠性. 毫无疑问,设计模式于 ...

  8. python3中的单例模式Singleton

    #!/usr/bin/env python # -*- coding: utf-8 -*- # @Date : 2019-01-21 09:09:09 # @Author : cdl (1217096 ...

  9. android开发常用的设计模式,android开发设计模式之——单例模式详解

    单例模式是设计模式中最常见也最简单的一种设计模式,保证了在程序中只有一个实例存在并且能全局的访问到.比如在Android实际APP 开发中用到的 账号信息对象管理, 数据库对象(SQLiteOpenH ...

  10. Java 设计模式之单例模式

    单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一.这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式. 这种模式涉及到一个单一的类,该类负责创建自己的对 ...

最新文章

  1. 基于某网站的信息爬取与保存_指定查询内容
  2. SmtpClient 身份验证失败(authentication failed) 的原因分析
  3. 如何在32位程序中突破地址空间限制使用超过4G的内存
  4. 激活层是每一层都有吗_每一个日出日落,都是岁月痕迹。这些在东软的第一次,你还记得吗...
  5. 什么是恶意软件?病毒,蠕虫,特洛伊木马等有害程序
  6. urule客户端和服务器配置
  7. 服务器xp系统网页打不开,xp系统打不开网页的具体方案
  8. 设置浏览器显示小于12px以下字体的三种方法
  9. 程序员做前端好还是做后台好?
  10. Blink SQL内置函数大全
  11. 直播推流拉流概念介绍
  12. 搭建wnmp开发环境
  13. android view设置按钮颜色_建议收藏!最全 Android 常用开源库总结!
  14. oracle打开scott用户_启用Oracle中的scott用户
  15. 抓强势股MACD大牛启动通达信指标公式 无未来 不加密
  16. reportROC 一行代码输出ROC曲线的各项统计数值及ROC曲线
  17. STM32中断控制LED灯闪烁
  18. 1,document.getelementbyid().value与document.getElementById().innerHTML区别
  19. 基于机器学习的人民日报和微博等与疫情有关话题数据两极情感分析
  20. 讨巧的站点简体/繁体中文切换方法

热门文章

  1. python安装pymssql等包时出现microsoft visual c++ 14.0 is required问题无需下载visualcppbuildtools的解决办法...
  2. scla-基础-函数-元组(0)
  3. js如何保证iframe里的内容,显示在父窗口
  4. Management reporter 2012 与AX 2012
  5. javascript实现的自适应宽度的瀑布流
  6. 客户组网服务案列_信息报道丨云浮支撑服务中心2020年第五期
  7. git 创建webpack项目_近期总结:手动搭建react项目,将项目从自己的库引入到新的项目中使用...
  8. 零售分析用vba还是python_数据分析?Excel、VBA和Python?营销套路还是大势所趋!...
  9. 用户组培训资料和资源
  10. Android WifiManager.WifiLock 简介