设计模式(1)-单例模式 及使用该模式改造的日志写入代码

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

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();
        /// <summary>
        /// 操作日志
        /// </summary>
        /// <param name="level"></param>
        /// <param name="time"></param>
        /// <param name="user"></param>
        /// <param name="browser"></param>
        /// <param name="ipAdrass"></param>
        /// <param name="operate"></param>
        /// <param name="detail"></param>
        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;
【安全】保密,你懂的。







评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值