个人感悟之CMS的精华和糟粕

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了个人感悟之CMS的精华和糟粕相关的知识,希望对你有一定的参考价值。

“神一样的队友”,“猪一样的队友”,“码农”,“工程狮”,“程序猿”,“看山不是山,看水不是水”,吧啦吧啦...

等一等。老码农你想说神马?

额,就是这个...那啥...,哎拖延症人人都有吧,为啥还有那么多毛病没改变呢。

最近啊,盼转正好久了,都快转正了。那对CMS有些自己的看法吧.。这里感谢公司这个平台,这个舞台。感谢队友们的支持和鼓励,希望作为老码农的我,能成为靠谱的队友,

给大伙些正能量和分享些经验,希望大伙少淌坑,为客户为公司多创造一些价值...

下面说说天天面对的CMS的精华和糟粕。

CMS的精华:

  • 1、面向对象思想之封装
  • 2、面向对象思想之继承
  • 3、设计模式的运用
  • 4、接口的运用之解耦
  • 5、泛型(泛型委托)应用
  • 6、玩多线程的姿势好帅
  • 7、IOCP完成端口)的使用
  • 8、牛逼哄哄的AOP面向切面编程
  • 9、动态编译技术的使用
  • 10、反射技术的使用
  • 11、编码生成器
  • 12、ORM和可视化模板设计器等

13、最最牛逼的能力是把前面十多项精华都玩转了,让大伙站在巨人肩上工作...

 

1、面向对象思想之封装

封装是最基本的面向对象思想之要素,底层处处是封装。写动态代码也应该尽量减少重复代码,该提炼成类的就提炼。

2、面向对象思想之继承

面向抽象编程这个思想是花费了好多年才慢慢正在养成的习惯。感觉做得还不够。架构师必须是偏执狂的,随时在践行各种面向对象的原则,不忘初心才能方得始终. 比如我们常写的流程类是继承了CoreBase,而CoreBase应该打上seale关键字,不允许被直接拿出来实例化。因为CoreBase职责是实现各种模板方法,并把这些方法贯通。面向的是抽象,不是具体的细节(某一工厂的流程). 使用继承就能重复利用基类封装的各种方法。总的来说继承是和封装一起使用的。多态不是必须的规范,代码里面肯定有。这里不单独拿出来说事。

3、设计模式的运用

CodeContext等使用了单例,还有工厂模式比如各种getXXXService等,细看还能发现观察者模式等的使用。觉得都挺应景的。这就是功力啊

4、接口的运用之解耦

接口我认为是用来解耦的最好东西,系统里各种IScanner,IPrinter,IPrintCore...

刚开始我非常惊讶,系统居然是代码和界面是完全分离的。敢情那么多年WINFORM编程都是白瞎,没有人家有追求啊。带来的好处太多了,话说当年我们哪个项目不是拖完控件,然后直接在事件属性里写对应的代码.low得不敢见人啊

5、泛型(泛型委托)应用

namespace ehsure.CMS.Core.Printer
{
    /// <summary>
    /// 用于与打印相关的事件回调
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="sender"></param>
    /// <param name="portKey"></param>
    /// <param name="e"></param>
    public delegate void DataPrintHandler<T>(object sender, string portKey, T e);
    /// <summary>
    /// 用于记录状态相关的事件回调
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="portKey"></param>
    /// <param name="data"></param>
    public delegate void DataStatusHandler<T>(object sender,string portKey,T e,bool data);
}

  public interface IScanner
    {
        int BaudRate { get; }
        void Close();
        event DataPrintHandler<TEventArgs<ScanData>> DataReceiving;
        event DataPrintHandler<TEventArgs<PrintData>> DataReceived;
        event FeedInfoHandler FeedInfo;
        bool IsOpen { get; }
        void Open();
}

  这个就不展开说了,其实还应该多使用泛型类来消除重复代码。泛型比较重要的是“约束”能力。

 

6、玩多线程的姿势好帅

这里说好帅是因为玩多线程需要特别小心一不小心线程同步等异步等技巧真是好高大上的技术啊。需要注意各种细节才能玩得好。具体那里好我也说不清楚,我想这是用看的。

系统里大量使用了IOCP(IoCompletionPort)是WINDOWS系统非常精华的一种模型。听说效率高得没谱,核心是操作系统级别的先进先出队列,反正出队入队都是调各种windows api.

  [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern SafeFileHandle CreateIoCompletionPort(IntPtr FileHandle, IntPtr ExistingCompletionPort, IntPtr CompletionKey, uint NumberOfConcurrentThreads);
        [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern bool GetQueuedCompletionStatus(SafeFileHandle CompletionPort, out uint lpNumberOfBytesTransferred, out IntPtr lpCompletionKey, out IntPtr lpOverlapped, uint dwMilliseconds);
        [DllImport("Kernel32", CharSet = CharSet.Auto)]
        private static extern bool PostQueuedCompletionStatus(SafeFileHandle CompletionPort, uint dwNumberOfBytesTransferred, IntPtr dwCompletionKey, IntPtr lpOverlapped);

我觉得在.net 2时代有这样的牛叉模型还真是幸福,IOCP还支持多线程并发的。好了这里跑题了。

  

7、IOCP完成端口)的使用

接着说IOCP,在CMS里CoreThreadPool的靓丽身影随处可见。这些应付多线程的利器啊。

    /// <summary>
    /// 自定义线程池类,不依赖.net Queue实现了先进先出处理队列里数据
    /// </summary>
    public class CoreThreadPool : IDisposable
    {
        /// <summary>
        /// 队列元素申明
        /// </summary>
        [StructLayout(LayoutKind.Sequential)]
        private class PoolData
        {
            /// <summary>
            /// 外部要求放入队列的数据
            /// </summary>
            public object Data;
            /// <summary>
            /// 需要执行的命令(Exit/Command(自定义))
            /// </summary>
            public CoreThreadPool.PoolCommand Command;
            public PoolData()
            {
                this.Command = CoreThreadPool.PoolCommand.Exit;
            }
            public PoolData(object data)
            {
                this.Data = data;
                this.Command = CoreThreadPool.PoolCommand.Command;
            }
            public PoolData(CoreThreadPool.PoolCommand cmd)
            {
                this.Command = cmd;
            }
        }
        protected enum PoolCommand
        {
            Command,
            Exit
        }
        protected SafeFileHandle complatePort;
        /// <summary>
        /// 线程池主线程
        /// </summary>
        protected Thread thread;
        protected volatile bool isOpened;
        [method: CompilerGenerated]
        [CompilerGenerated]
        public event Action<object> Exceute;
        [method: CompilerGenerated]
        [CompilerGenerated]
        public event Action<object> ExitExceute;
        /// <summary>
        /// 线程池是否正在运行
        /// </summary>
        public bool IsOpened
        {
            get
            {
                return this.isOpened;
            }
            set
            {
                this.isOpened = value;
            }
        }
        [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern SafeFileHandle CreateIoCompletionPort(IntPtr FileHandle, IntPtr ExistingCompletionPort, IntPtr CompletionKey, uint NumberOfConcurrentThreads);
        [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern bool GetQueuedCompletionStatus(SafeFileHandle CompletionPort, out uint lpNumberOfBytesTransferred, out IntPtr lpCompletionKey, out IntPtr lpOverlapped, uint dwMilliseconds);
        [DllImport("Kernel32", CharSet = CharSet.Auto)]
        private static extern bool PostQueuedCompletionStatus(SafeFileHandle CompletionPort, uint dwNumberOfBytesTransferred, IntPtr dwCompletionKey, IntPtr lpOverlapped);
        /// <summary>
        /// 启动线程池的主线程
        /// </summary>
        public void Start()
        {
            isOpened = true;
            if (thread != null)
            {
                throw new Exception("线程池已经是启动状态!");
            }
            complatePort = CreateIoCompletionPort(new IntPtr(-1), IntPtr.Zero, IntPtr.Zero, 0u);
            if (complatePort.IsInvalid)
            {
                throw new Exception(string.Format("创建IOCP出错!原因是:{0}", Marshal.GetLastWin32Error().ToString()));
            }
            thread = new Thread(new ParameterizedThreadStart(this.Run));
            thread.Start(complatePort);
        }
        /// <summary>
        /// 外部提交数据对象到队列
        /// </summary>
        /// <param name="data"></param>
        public void Post(object data)
        {
            PostData(new CoreThreadPool.PoolData(data));
        }
        /// <summary>
        /// 线程池主线程执行逻辑
        /// </summary>
        /// <param name="CompletionPortID"></param>
        private void Run(object CompletionPortID)
        {
            SafeFileHandle completionPort = (SafeFileHandle)CompletionPortID;            
            while (IsOpened)
            {
                uint num;
                IntPtr intPtr;
                IntPtr value;
                //从队列里取出最前面的对象
                CoreThreadPool.GetQueuedCompletionStatus(completionPort, out num, out intPtr, out value, 4294967295u);
                if (num > 0u)
                {
                    GCHandle gCHandle = GCHandle.FromIntPtr(value);
                    CoreThreadPool.PoolData poolData = (CoreThreadPool.PoolData)gCHandle.Target;
                    gCHandle.Free();
                    if (poolData.Command != CoreThreadPool.PoolCommand.Command)
                    {
                        IsOpened = false;
                        break;
                    }
                    RaiseExecute(poolData.Data);
                }
            }
            RaiseExitExecute("线程池已经停止。");
            isOpened = false;
            thread = null;
        }
        /// <summary>
        /// 触发Execute事件
        /// </summary>
        /// <param name="data"></param>
        private void RaiseExecute(object data)
        {
            Exceute?.Invoke(data);
        }
        /// <summary>
        /// 触发ExitExecute事件
        /// </summary>
        /// <param name="data"></param>
        private void RaiseExitExecute(object data)
        {
            ExitExceute?.Invoke(data);
        }
        /// <summary>
        /// 结束线程池主线程
        /// </summary>
        public void Stop()
        {
            PostData(new PoolData(PoolCommand.Exit));
            IsOpened = false;
        }
        /// <summary>
        /// 内部提交数据到线程池队列中
        /// </summary>
        /// <param name="data"></param>
        private void PostData(PoolData data)
        {
            if (complatePort.IsClosed)
            {
                return;
            }
            GCHandle value = GCHandle.Alloc(data);
            PostQueuedCompletionStatus(complatePort, (uint)IntPtr.Size, IntPtr.Zero, GCHandle.ToIntPtr(value));
        }
        public void Dispose()
        {
            if (this.thread != null && this.thread.ThreadState != System.Threading.ThreadState.Stopped)
            {
                this.Stop();
            }
        }
    }

上面代码是我反编译出来的。CMS是封装在dll里了,平常看不到源码的。

 

/// <summary>
    /// 自定义线程池类,使用ConcurrentQueue实现了先进先出处理队列里数据
    /// </summary>
    public class CoolThreadPool : IDisposable
    {
        protected ConcurrentQueue<string> queue = new ConcurrentQueue<string>();
        protected Thread thread;
        private volatile bool isOpened;
        public bool IsOpened
        {
            get
            {
                return isOpened;
            }
        }

        public event Action<string> Exceute;
        public event Action StopedExceute;
        /// <summary>
        /// 启动线程池的主线程
        /// </summary>
        public void Start()
        {
            if (thread != null)
            {
                throw new Exception("线程池已经是启动状态!");
            }
            thread = new Thread(Run);
            isOpened = thread != null;
            thread.Start();
        }

        /// <summary>
        /// 线程池主线程执行逻辑
        /// </summary>
        private void Run()
        {
            while (isOpened)
            {
                if (queue.Count > 0)
                {
                    string temp = null;
                    queue.TryDequeue(out temp);
                    if (!string.IsNullOrEmpty(temp))
                    {
                        RaiseExecute(temp);
                    }
                    else break;
                }
            }
            isOpened = false;
            thread = null;
            RaiseStopedExceute();
        }

        /// <summary>
        /// 触发Execute事件
        /// </summary>
        /// <param name="data"></param>
        private void RaiseExecute(string data)
        {
            Exceute?.Invoke(data);
        }

        /// <summary>
        /// 触发停止Execute事件
        /// </summary>
        /// <param name="data"></param>
        private void RaiseStopedExceute()
        {
            StopedExceute?.Invoke();
        }

        /// <summary>
        /// 结束线程池主线程
        /// </summary>
        public void Stop()
        {
            PostData(null);
            isOpened = false;
        }

        /// <summary>
        /// 外部提交数据对象到队列
        /// </summary>
        /// <param name="data"></param>
        public void Post(string data)
        {
            PostData(data);
        }

        /// <summary>
        /// 内部提交数据到线程池队列中
        /// </summary>
        /// <param name="data"></param>
        private void PostData(string data)
        {
            queue.Enqueue(data);
        }

        public void Dispose()
        {
            if (this.thread != null && this.thread.ThreadState != System.Threading.ThreadState.Stopped)
            {
                this.Stop();
            }
        }
    }

8、牛逼哄哄的AOP面向切面编程

主程序的权限和界面的各种校验都是使用了各种拦截器。这个由于没有深入研读源码,待以后体会到再分享。

9、动态编译技术的使用

 

 /// <summary>
        /// 动态编译动态脚本
        /// </summary>
        /// <param name="content">动态脚本对象</param>
        /// <returns>返回程序集实例</returns>
        static Assembly BuildDynamicPrintCore(DynamicScript content)//(DynamicPrinterConfig content)
        {
            CoreBuilder cb = new CoreBuilder();
            cb.Header = header;
            cb.Bottom = bottom;
            cb.UseingText = content.UsingText;
            cb.ReferenceText = content.ReferenceText;
            return cb.Compile(content.Script);
        }
        /// <summary>
        /// 动态编译动态脚本
        /// </summary>
        /// <param name="content">动态脚本对象</param>
        /// <returns>返回程序集实例</returns>
        static Assembly BuildDynamicPrintCore(DynamicPrinterConfig content)
        {
            CoreBuilder cb = new CoreBuilder();
            cb.Header = header;
            cb.Bottom = bottom;
            cb.UseingText = content.UsingText;
            cb.ReferenceText = content.ReferenceText;
            return cb.Compile(content.Script);
        }
        /// <summary>
        /// 动态脚本引用的程序集头
        /// </summary>
        const string header = @"
using System;
using System.Collections.Generic;
using System.Threading;
using System.Timers;
using EES.Common;
using EES.Common.Data;
using ehsure.CMS.Core.Card.DI;
using ehsure.CMS.Core.Card.DO;
using ehsure.CMS.Core.Codes;
using ehsure.CMS.Core.Printer;
using ehsure.CMS.Core.Printer.Saver;
using ehsure.CMS.Core.Scanner;
using ehsure.CMS.CoreData;
using ehsure.CMS.CoreData.Common;
using ehsure.CMS.CoreData.Config;
using ehsure.CMS.CoreData.Exceptions;
using ehsure.CMS.Core;
using System.Text;
using ehsure.CMS.Common;
using ehsure.CMS.Common.Config;
using System.Linq;
using System.Net.Sockets;
using EES.Common.Query;

using System.Text.RegularExpressions;
namespace ehsure.CMS.Core
{
    public class DynamicPrintCore : CoreBase
    {
        public DynamicPrintCore()
            : base()
        { }

        public static IPrintCore CreateNew()
        {
            return CoreBase.CreateNew(typeof(DynamicPrintCore));
        }


";

11、编码生成器

这是生码必须要使用的模块。这么的人性化也是颠覆了我的认知。想起来在某项目里写的防伪码生成器真是比这个差远了,不好意思见人啊。

12、ORM和可视化模板设计器等

NH,EF等ORM都有用过,但是CMS使用的ORM还是非常佩服,在.net 2时代能弄出来如此像NH,linq的东西还有代码生成器。表示惊为天人,叹为观止。

后记

写文章还挺费时间,关于糟粕我打算另写一篇吧。太长看起来费劲。这个糟粕我还需要收集呢,也许看到的精华有点多,但是希望最终要吸收这些精华,弃糟粕,在CMS代码江湖里贡献一份力量。谢谢大家!

以上是关于个人感悟之CMS的精华和糟粕的主要内容,如果未能解决你的问题,请参考以下文章

redis前传集思广益之quicklist,取其精华去其糟粕

redis前传集思广益之quicklist,取其精华去其糟粕

「青莲科普」R语言系列——取其精华,去其糟粕的ggplot2你会用吗?

NestJS 7.x 折腾记: 异常过滤器,取其精华去其糟粕!比如响应异常数据的包装~

企业自媒体营销,内容运营方法论终极指南

[博士论文写作总结]系列 写作前的思考