设计模式(1)-单例模式 及使用该模式改造的日志写入代码
发布日期:2021-11-12 00:26:29 浏览次数:2 分类:技术文章

本文共 7425 字,大约阅读时间需要 24 分钟。

单例模式是保证一个类只有一个实例的一种实现方法,那么怎么实现呢?

1、私有化构造函数。

2、提供一个全局访问方法用于获取实例。

为了有直观的印象,直接上代码:

public class ClassWriteLog{        // 定义一个静态变量来保存类的实例        private static ClassWriteLog clsWriteLog;        // 定义一个标识确保线程同步        private static readonly object locker = new object();        // 定义一个静态变量来保存类的实例        // 私有化构造函数        private ClassWriteLog()        {        }             // 定义一个方法获取类的对象        public static ClassWriteLog GetInstance()        {            // 如果类的实例不存在则创建,否则直接返回            if (clsWriteLog == null)            {                // 锁定创建对象的过程,如果第一个线程判断对象为空,则创建对象                // 第二个线程到达时,对象也可能为空,那么在锁定的过程中,                // 再进行一次判断,如果第一个线程对象创建成功,那么就不用再创建了。                lock (locker)                {                    if (clsWriteLog == null)                    {                        clsWriteLog = new ClassWriteLog();                    }                }            }            return clsWriteLog;        }}

这样通过GetInstance方法就可以保证代码中,获取ClassWriteLog类都获取的是同一个对象而不会重新生成了。

-------该部分引用自  https://www.cnblogs.com/damsoft/p/6105122.html   ----------------------------------

        那么,为什么要使用单例模式呢?

单例模式只允许创建一个对象,因此节省内存,加快对象访问速度,因此对象需要被公用的场合适合使用,

如多个模块使用同一个数据源连接对象等等

主要的使用场景有:

1、需要频繁实例化然后销毁的对象。

2、创建对象时好事过多或者资源消耗过多,但又经常用到的对象。

3、有状态的工具类对象。

4、频繁访问数据库活文件的对象。

以下都是单例模式的经典使用场景: 

1、资源共享的情况下,避免由于资源操作时导致的性能或损耗等。如上述中的日志文件,应用配置。
2、控制资源的情况下,方便资源之间的互相通信。如线程池等。
3、有状态的工具类对象。

4、频繁访问数据库活文件的对象。 

以下都是单例模式的经典使用场景: 

    1.资源共享的情况下,避免由于资源操作时导致的性能或损耗等。如上述中的日志文件,应用配置。 

    2.控制资源的情况下,方便资源之间的互相通信。如线程池等。

应用场景举例: 

    1.外部资源:每台计算机有若干个打印机,但只能有一个PrinterSpooler,以避免两个打印作业同时输出到打印机。内部资源:

大多数软件都有一个(或多个)属性文件存放系统配置,这样的系统应该有一个对象管理这些属性文件 

    2. Windows的Task Manager(任务管理器)就是很典型的单例模式(这个很熟悉吧),想想看,是不是呢,

你能打开两个windows task manager吗? 不信你自己试试看哦~ 

    3. windows的Recycle Bin(回收站)也是典型的单例应用。在整个系统运行过程中,回收站一直维护着仅有的一个实例。 

    4. 网站的计数器,一般也是采用单例模式实现,否则难以同步。 

    5. 应用程序的日志应用,一般都何用单例模式实现,这一般是由于共享的日志文件一直处于打开状态,因为只能有一个实例去操作,

否则内容不好追加。 

    6. Web应用的配置对象的读取,一般也应用单例模式,这个是由于配置文件是共享的资源。 

    7. 数据库连接池的设计一般也是采用单例模式,因为数据库连接是一种数据库资源。数据库软件系统中使用数据库连接池,

主要是节省打开或者关闭数据库连接所引起的效率损耗,这种效率上的损耗还是非常昂贵的,因为何用单例模式来维护,

就可以大大降低这种损耗。 

    8. 多线程的线程池的设计一般也是采用单例模式,这是由于线程池要方便对池中的线程进行控制。 

    9. 操作系统的文件系统,也是大的单例模式实现的具体例子,一个操作系统只能有一个文件系统。 

    10. HttpApplication 也是单位例的典型应用。熟悉ASP.Net(IIS)的整个请求生命周期的人应该知道HttpApplication也是单例模式,

所有的HttpModule都共享一个HttpApplication实例. 

优点: 
    1.在单例模式中,活动的单例只有一个实例,对单例类的所有实例化得到的都是相同的一个实例。这样就 防止其它对象对自己的实例化,

确保所有的对象都访问一个实例 
    2.单例模式具有一定的伸缩性,类自己来控制实例化进程,类就在改变实例化进程上有相应的伸缩性。 
    3.提供了对唯一实例的受控访问。 
    4.由于在系统内存中只存在一个对象,因此可以 节约系统资源,当 需要频繁创建和销毁的对象时单例模式无疑可以提高系统的性能。 
    5.允许可变数目的实例。 
    6.避免对共享资源的多重占用。 
缺点: 
    1.不适用于变化的对象,如果同一类型的对象总是要在不同的用例场景发生变化,单例就会引起数据的错误,不能保存彼此的状态。 
    2.由于单例模式中没有抽象层,因此单例类的扩展有很大的困难。 
    3.单例类的职责过重,在一定程度上违背了“单一职责原则”。 
    4.滥用单例将带来一些负面问题,如为了节省资源将数据库连接池对象设计为的单例类,可能会导致共享连接池对象的程序过多而出现连接池溢出;

如果实例化的对象长时间不被利用,系统会认为是垃圾而被回收,这将导致对象状态的丢失。 

-----------------------------------引用完毕  ------------------------------------------------------

以上这部分内容可以加深对单例模式的理解。其实主要就是两点:

1、单例模式可以减少资源的消耗,避免资源的多重占用;

2、不利于扩展,并且类不适合职责过重;

3、使用共享资源时,建议使用单例模式,避免冲突。

我根据单例模式的学习,修改了之前工作中用到的记录文本日志的类。接着上代码

// 获取日志文件的类,使用了单例模式    public class ClassWriteLog    {        // 定义一个静态变量来保存类的实例        private static ClassWriteLog clsWriteLog;        // 定义一个标识确保线程同步        private static readonly object locker = new object();        //private string FilePath;        public FileInfo fi;        // 私有化构造函数        private ClassWriteLog()        {            string FilePath = AppDomain.CurrentDomain.BaseDirectory;            // 程序目录下log文件夹 如果不存在则新建            FilePath = AppDomain.CurrentDomain.BaseDirectory + "log";            if (!Directory.Exists(FilePath))            {                Directory.CreateDirectory(FilePath);            }            // log文件夹中按照年和月分开存储 如果不存在则新建            FilePath = FilePath + "\\" + DateTime.Now.ToString("yyMM");            if (!Directory.Exists(FilePath))            {                Directory.CreateDirectory(FilePath);            }            // 根据日期生成不同日期的文件 不存在则生成文件            FilePath = FilePath + "\\" + DateTime.Now.ToString("dd") + ".txt";            if (!File.Exists(FilePath))            {                FileStream fs = File.Create(FilePath);                fs.Close();            }            fi = new FileInfo(FilePath);                    }        // 定义一个方法获取类的对象        public static ClassWriteLog GetInstance()        {            // 如果类的实例不存在则创建,否则直接返回            if (clsWriteLog == null)            {                // 锁定创建对象的过程,如果第一个线程判断对象为空,则创建对象                // 第二个线程到达时,对象也可能为空,那么在锁定的过程中,                // 再进行一次判断,如果第一个线程对象创建成功,那么就不用再创建了。                lock (locker)                {                    if (clsWriteLog == null)                    {                        clsWriteLog = new ClassWriteLog();                    }                }            }            return clsWriteLog;        }     }

// 写入日志的操作类    public class LogWriteOper    {        static readonly object syncObjectWrite = new object();        public static void Write(object Content)        {            lock (syncObjectWrite)            {                // ClassWriteLog.GetInstance()获取单例模式中的对象中的FileInfo类型的属性                using (StreamWriter sw = ClassWriteLog.GetInstance().fi.AppendText())                {                    sw.Write(Content.ToString() + "\r\n");                    sw.Close();                }            }        }    }

下面代码是使用多线程方式调用写入日志的方法,用于测试写入的情况

Thread thread1 = null;            for (int iIndex = 0; iIndex < 100; iIndex++)            {                thread1 = new Thread(new ParameterizedThreadStart(LogWriteOper.Write));                thread1.Start(iIndex.ToString());            }            Response.Write("完成");

其实之前写入文本文件日志的时候,没有采用单例模式,也给大家看看,其实使用单例模式真的可以减少资源的消耗,比如我创建文件的那部分内容,

每次写入的时候都要执行,真的蛮耗资源的。

教训啊,同学们。

private static readonly object obj = new object();        ///         /// 操作日志        ///         ///         ///         ///         ///         ///         ///         ///         public static void WriteLogs(string strContent)        {            lock (obj)            {                string path = AppDomain.CurrentDomain.BaseDirectory;
if (!string.IsNullOrEmpty(path))                {                    path = AppDomain.CurrentDomain.BaseDirectory + "log";                    if (!Directory.Exists(path))                    {                        Directory.CreateDirectory(path);                    }                    path = path + "\\" + DateTime.Now.ToString("yyMM");                    if (!Directory.Exists(path))                    {                        Directory.CreateDirectory(path);                    }                    path = path + "\\" + DateTime.Now.ToString("dd") + ".txt";                    if (!File.Exists(path))                    {                        FileStream fs = File.Create(path);                        fs.Close();                    }                    if (File.Exists(path))                    {                        StreamWriter sw = new StreamWriter(path, true, System.Text.Encoding.Default);                        sw.WriteLine(strContent + "\r\n\r\n");                        sw.Close();                    }                }            }        }

知行办公,专业移动办公平台 https://zx.naton.cn/
【总监】十二春秋之,3483099@qq.com;
【Master】zelo,616701261@qq.com;
【运营】运维艄公,897221533@qq.com;
【产品设计】流浪猫,364994559@qq.com;
【体验设计】兜兜,2435632247@qq.com;
【iOS】淘码小工,492395860@qq.com;iMcG33K,imcg33k@gmail.com;
【Android】人猿居士,1059604515@qq.com;思路的顿悟,1217022114@qq.com;
【java】首席工程师MR_W,feixue300@qq.com;
【测试】土镜问道,847071279@qq.com;
【数据】fox009521,42151960@qq.com;
【安全】保密,你懂的。

转载地址:https://blog.csdn.net/fox009521/article/details/78485631 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:php发送post请求的三种方法
下一篇:七大常见排序算法总结

发表评论

最新留言

能坚持,总会有不一样的收获!
[***.219.124.196]2024年04月07日 05时35分58秒