文章出處
View Code
View Code
View Code
View Code
View Code
文章列表
之前的講過兩篇關于日志組件的文章,分別是《第一回 日志記錄組件之自主的Vlog》和《第三回 日志記錄組件之log4net》,而今天主要說一下我自己開發的另一種日志組件Logger.Core,它也屬于面試AOP(橫切關注點)的一部分,這個組件對于一些想學習設計模式的同學來說,無疑是一個大餐!Logger.Core項目里內含了策略模式,模版方法模式,工廠模式和單例模式,可以說,最常用的模式都用到了,而它們在這個項目里都起到了什么作用,什么時候用到它們呢,這些答案相信在看完我的文章之后,您會有一個明確的答案的。
一 面向接口編程與多態
面向接口編程,是實現軟件解耦的靈魂,也是實現多態的方法之一,日志項目有統一的接口規范

/// <summary> /// 日志功能接口規范 /// </summary> public interface ILogger { /// <summary> /// 記錄代碼運行時間 /// </summary> /// <param name="message">消息</param> /// <param name="action">所測試的代碼塊</param> /// <param name="fileName">日志文件名</param> void Logger_Timer(string message, Action action, string fileName); /// <summary> /// 記錄代碼運行時間,日志文件名以codeTime開頭的時間戳 /// </summary> /// <param name="message">消息</param> /// <param name="action">所測試的代碼塊</param> void Logger_Timer(string message, Action action); /// <summary> /// 記錄代碼運行異常 /// </summary> /// <param name="message">消息</param> /// <param name="action">要添加try...catch的代碼塊</param> /// <param name="fileName">日志文件名</param> void Logger_Exception(string message, Action action, string fileName); /// <summary> /// 記錄代碼運行異常,日志文件名以Exception開頭的時間戳 /// </summary> /// <param name="message">消息</param> /// <param name="action">要添加try...catch的代碼塊</param> void Logger_Exception(string message, Action action); /// <summary> /// 將message記錄到日志文件 /// </summary> /// <param name="message"></param> void Logger_Info(string message); /// <summary> /// 將message記錄到名為fileName的日志文件 /// </summary> /// <param name="message"></param> /// <param name="fileName"></param> void Logger_Info(string message, string fileName); }
二 繼承與面向對象
繼承是面向對象的三大特性之一,有了它,面向對象才有了層次感,將公共的功能點從各個派生類抽出,提取到基類中

/// <summary> /// 日志核心基類 /// 模版方法模式,對InputLogger開放,對其它日志邏輯隱藏,InputLogger可以有多種實現 /// </summary> internal abstract class LoggerBase : ILogger { private string _defaultLoggerName = DateTime.Now.ToString("yyyyMMddhh") + ".log"; /// <summary> /// 日志文件地址 /// 優化級為mvc方案地址,網站方案地址,console程序地址 /// </summary> protected string FileUrl { get { try { return System.Web.HttpContext.Current.Server.MapPath("/Logger/" + System.Web.HttpContext.Current.Request.RequestContext.RouteData.Values["controller"].ToString() + "/" + System.Web.HttpContext.Current.Request.RequestContext.RouteData.Values["action"].ToString() + "/"); //例如:c:\\project\\Logger\\Home\\Index\\ } catch (NullReferenceException) { try { return System.Web.HttpRuntime.AppDomainAppPath + "LoggerDir\\"; //例如:c:\\project\\ } catch (ArgumentNullException) { return Environment.CurrentDirectory + "\\LoggerDir\\"; //例如:c:\\project\\bin\\debug } } } } protected abstract void InputLogger(string message, string fileName); #region ILogger 成員 public void Logger_Timer(string message, Action action, string fileName) { StringBuilder str = new StringBuilder(); Stopwatch sw = new Stopwatch(); sw.Restart(); str.Append(message); action(); str.Append("代碼段運行時間:" + sw.ElapsedMilliseconds + "毫秒"); InputLogger(str.ToString(), string.IsNullOrWhiteSpace(fileName) ? "CodeTime" + _defaultLoggerName : fileName); sw.Stop(); } public void Logger_Timer(string message, Action action) { Logger_Timer(message, action, null); } public void Logger_Exception(string message, Action action, string fileName) { try { action(); } catch (Exception ex) { InputLogger("代碼段出現異常,信息為" + ex.Message, string.IsNullOrWhiteSpace(fileName) ? "Exception" + _defaultLoggerName : fileName); } } public void Logger_Exception(string message, Action action) { Logger_Exception(message, action, null); } public void Logger_Info(string message) { InputLogger(message, null); } public void Logger_Info(string message, string fileName) { InputLogger(message, string.IsNullOrWhiteSpace(fileName) ? "Logger" + _defaultLoggerName : fileName); } #endregion }
三 模版方式模式規定具體流程,抽象個性化方法
對于個性化的操作聲明為抽象方法,在基類中實現統一的操作流程,在各個派生類中去實現個性化的模塊,這正是模版方式模式的體現
四 策略模式以多種方式實現某個功能
對于文件持久化的方式有很多,而你可以分別去實現它,不知不覺中,我們正在使用策略模式來開發應用程序
普通持久化

/// <summary> /// 以普通的文字流的方式寫日志 /// </summary> internal class NormalLogger : LoggerBase { protected override void InputLogger(string message, string fileName) { string filePath = FileUrl + (fileName ?? "logger.log"); string dir = filePath.Substring(0, filePath.LastIndexOf("\\")); if (!System.IO.Directory.Exists(dir)) { System.IO.Directory.CreateDirectory(dir); } using (System.IO.StreamWriter srFile = new System.IO.StreamWriter(filePath, true)) { srFile.WriteLine(message); srFile.Close(); srFile.Dispose(); } } }
log4net實現日志分級的持久化

/// <summary> /// Function:以log4net組件的方式寫日志 /// Remark:日志記錄方法可以使用第三方組件,如log4net /// Author:zhangzhanling /// Blogs:www.cnblogs.com/lori /// </summary> internal class Log4Logger : LoggerBase { /// <summary> /// log4net配置文件路徑 /// </summary> static string _logConfig = System.Web.HttpContext.Current.Server.MapPath("/log4net.config"); static Log4Logger() { log4net.Config.XmlConfigurator.ConfigureAndWatch(new System.IO.FileInfo(_logConfig)); } #region Prviate Methods /// <summary> /// 寫日志方法 /// </summary> /// <param name="message"></param> /// <param name="fileName"></param> protected override void InputLogger(string message, string fileName) { string filePath = FileUrl + fileName; var iLog = log4net.LogManager.GetLogger("Core.Logger"); ChangeLog4netLogFileName(iLog, filePath, message); } private void ChangeLog4netLogFileName(log4net.ILog iLog, string filePath, string message) { log4net.Core.LogImpl logImpl = iLog as log4net.Core.LogImpl; if (logImpl != null) { var ac = ((log4net.Repository.Hierarchy.Logger)logImpl.Logger).Appenders; var rfa = ac[0] as log4net.Appender.RollingFileAppender; if (rfa != null) { string dir = filePath.Substring(0, filePath.LastIndexOf("\\")); if (!System.IO.Directory.Exists(dir)) { System.IO.Directory.CreateDirectory(dir); } rfa.File = filePath; // 更新Writer屬性 rfa.Writer = new System.IO.StreamWriter(rfa.File, rfa.AppendToFile, rfa.Encoding); rfa.Writer.WriteLine(message); rfa.Writer.Close(); rfa.Writer.Dispose(); rfa.Close(); } } } #endregion }
五 工廠模式動態生產對象,單例模式保持對象實例的唯一性
當我們以多種方式實現了對日志的持久化后,我們可以通過工廠模式動態的在這些持久化方式之間實現切換,對象實現單例之后,減少了內存開銷,使對象的屬性成為了全局性屬性!

/// <summary> /// 日志生產類 /// Singleton模式和策略模式和工廠模式 /// </summary> public class LoggerFactory : ILogger { /// <summary> /// 對外不能創建類的實例 /// </summary> private LoggerFactory() { string loggerType = System.Configuration.ConfigurationManager.AppSettings["LoggerType"] ?? "NormalLogger"; switch (loggerType) { case "NormalLogger": iLogger = new NormalLogger(); break; case "Log4Logger": iLogger = new Log4Logger(); break; default: throw new ArgumentException("日志方法不正確,目前只支持NormalLogger和Log4Logger"); } //(ILogger)Assembly.Load("Logger.Core").CreateInstance("Logger.Core." + className.Trim()); } #region Logger有多種實現時,需要使用Singleton模式 private static object lockObj = new object(); private static LoggerFactory instance = null; private ILogger iLogger = null; /// <summary> /// Get singleton instance of IoCFactory /// </summary> public static LoggerFactory Instance { get { if (instance == null) { lock (lockObj) { if (instance == null) { instance = new LoggerFactory(); } } } return instance; } } #endregion #region ILogger 成員 public void Logger_Timer(string message, Action action, string fileName) { iLogger.Logger_Timer(message, action, fileName); } public void Logger_Timer(string message, Action action) { iLogger.Logger_Timer(message, action); } public void Logger_Exception(string message, Action action, string fileName) { iLogger.Logger_Exception(message, action, fileName); } public void Logger_Exception(string message, Action action) { iLogger.Logger_Exception(message, action); } public void Logger_Info(string message) { iLogger.Logger_Info(message); } public void Logger_Info(string message, string fileName) { iLogger.Logger_Info(message, fileName); } #endregion }
最后有一句話送給大家:堅持,其實就是一種勝利!
文章列表
全站熱搜