[Architecture Pattern] Device Projection (下)

接续...

[Architecture Pattern] Device Projection 模式 (上)

实做 :

范列下载 :

DeviceProjectionSample点此下载

范列逻辑 :

下面图片是范例程序执行的结果。

主要的参与者有:

LightDevice.exe
-仿真远程设备的程序,采用TCP联机连接LightMaster。
-窗体上灯号数据的图像,可透过右侧灯号按钮做开关。
-窗体上灯号数据的图像,接受LightMaster传送来的指令做开关。
-每300ms会将灯号数据传送到LightMaster。

LightMaster.exe
-映像远程设备的程序,采用TCP联机聆听LightDevice连接。
-窗体上LightDevice数据列表,会映像LightDevice连接状态、灯号数据。
-选择窗体上一笔LightDevice数据后,可透过右侧灯号按钮对LightDevice传送指令做开关。

透过下面的图片说明,简单说明范例程序的互动流程。

模式封装 :

在建立范例之前,先将Tcp联机、范例用的通讯指令,套用Device Projection 模式来实做。
在实做的过程中发现Device Projection 模式,是可以精炼出可重用的模式封装。
所以在实做模式之前,先封装可重用的模式封装。让后续实做可以直接套用,缩短实做花费的时间。

模式封装的程序代码,主要是封装Device Projection 模式的运作逻辑。
相关细节可以下载范例档案参考,或是参考[Architecture Pattern] Device Projection 模式 (上)

模式实做 :

因为已经封装可重用的泛型,接下来的实做直接套用模式泛型。
主要的参与者有:

ILightDeviceSketch
-继承自IDeviceSketch的接口,提供IPEndPoint属性做为标识属性
-以接口形式出现,主要是要将通讯协议实做与整个模式做隔离。

namespace DeviceProjectionSample
{
    public interface ILightDeviceSketch : IDeviceSketch
    {
        // Properties
        IPEndPoint IPEndPoint { get; }
    }    
}

ILightDeviceControl
-继承自IDeviceControl的接口,提供LightStatus属性、灯号开关方法
-以接口形式出现,主要是要将通讯协议实做与整个模式做隔离。

namespace DeviceProjectionSample
{
    public interface ILightDeviceControl : IDeviceControl
    {
        // Properties
        bool LightStatus { get; }


        // Methods
        void OpenLight();

        void CloseLight();
    }
}

LightDevice
-继承自Device的对象。
-实际提供给外部模块使用的对象。
-封装转接 ILightDeviceSketch、ILightDeviceControl所提供的属性跟方法。
-不支持额外IDeviceSketch当作属性来源,更新Device属性数据。

namespace DeviceProjectionSample
{
    public class LightDevice : Device<ILightDeviceSketch, ILightDeviceControl>
    {
        // Constructor 
        public LightDevice(ILightDeviceSketch deviceSketch, ILightDeviceControl deviceControl)
            : base(deviceSketch, deviceControl) { }


        // Properties
        public IPEndPoint IPEndPoint { get { return this.DeviceSketch.IPEndPoint; } }

        public bool LightStatus { get { return this.DeviceControl.LightStatus; } }


        // Methods
        protected override bool ImportProperty(ILightDeviceSketch deviceSketch)
        {
            return false;
        }


        public void OpenLight()
        {
            this.DeviceControl.OpenLight();
        }

        public void CloseLight()
        {
            this.DeviceControl.CloseLight();
        }
    }
}

LightDeviceCollection
-继承自DeviceCollection的对象。
-模式内部Device对象实际存放的集合对象。
-除了提供模式内部存取之外,也开放给外部模块使用。

namespace DeviceProjectionSample
{
    public class LightDeviceCollection : DeviceCollection<LightDevice, ILightDeviceSketch, ILightDeviceControl>
    {
        // Fields
        private readonly List<LightDevice> _deviceList = new List<LightDevice>();  


        // Methods
        protected override LightDevice GetDevice(ILightDeviceSketch deviceSketch)
        {
            #region Require

            if (deviceSketch == null) throw new ArgumentNullException();

            #endregion
            foreach (LightDevice device in _deviceList)
            {
                if (device.IPEndPoint.ToString() == deviceSketch.IPEndPoint.ToString())
                {
                    return device;
                }
            }
            return null;
        }

        protected override void AddDevice(LightDevice device)
        {
            #region Require

            if (device == null) throw new ArgumentNullException();

            #endregion
            _deviceList.Add(device);
        }

        protected override void RemoveDevice(LightDevice device)
        {
            #region Require

            if (device == null) throw new ArgumentNullException();

            #endregion
            _deviceList.Remove(device);
        }

        public override IEnumerator<LightDevice> GetEnumerator()
        {
            return _deviceList.GetEnumerator();
        }
    }
}

LightDeviceManager
-继承自DeviceManager的对象。
-主要提供映像远程设备LightDevice。

namespace DeviceProjectionSample
{
    public class LightDeviceManager : DeviceManager<LightDevice, ILightDeviceSketch, ILightDeviceControl>
    {
        // Constructor 
        public LightDeviceManager(IDeviceFactory<LightDevice, ILightDeviceSketch, ILightDeviceControl> deviceFactory, IDeviceSketchExplorer<ILightDeviceSketch> deviceSketchExplorer)
            : this(new LightDeviceCollection(), deviceFactory, deviceSketchExplorer)
        {

        }

        private LightDeviceManager(LightDeviceCollection deviceCollection, IDeviceFactory<LightDevice, ILightDeviceSketch, ILightDeviceControl> deviceFactory, IDeviceSketchExplorer<ILightDeviceSketch> deviceSketchExplorer)
            : base(deviceCollection, deviceFactory, deviceSketchExplorer)
        {
            #region Require

            if (deviceCollection == null) throw new ArgumentNullException();

            #endregion
            this.DeviceCollection = deviceCollection;            
        }


        // Properties
        public LightDeviceCollection DeviceCollection { get; private set; }
    }
}

MessagingClient
-封装TcpClient的对象。
-提供较方便的数据输入输出功能。

namespace DeviceProjectionSample.Concretion
{
    public class MessagingClient : IDisposable
    {
        // Fields
        private readonly SynchronizationContext _syncContext = null;

        private readonly TcpClient _tcpClient = null;

        private readonly NetworkStream _networkStream = null;

        private readonly Thread _readThread = null;

        private bool _isDisposed = false;


        // Constructor 
        public MessagingClient(SynchronizationContext syncContext, TcpClient tcpClient)
        {
            #region Require

            if (syncContext == null) throw new ArgumentNullException();
            if (tcpClient == null) throw new ArgumentNullException();

            #endregion
            _syncContext = syncContext;
            _tcpClient = tcpClient;
            _networkStream = tcpClient.GetStream();
            _readThread = new Thread(this.ReadCommand);           
        }

        public void Start()
        {
            // Start
            _readThread.Start();
        }

        public void Dispose()
        {
            // Require
            if (_isDisposed == true) return;
            _isDisposed = true;

            // Dispose
            _networkStream.Dispose();
            _tcpClient.Close();
            _readThread.Join();
        }


        // Properties
        public IPEndPoint IPEndPoint
        {
            get
            {
                return _tcpClient.Client.RemoteEndPoint as IPEndPoint;
            }
        }


        // Methods      
        private void ReadCommand(object obj)
        {
            try
            {
                int commandCode = 0;
                do
                {
                    commandCode = _networkStream.ReadByte();
                    if (commandCode >= 0)
                    {
                        this.OnCommandArrived((byte)commandCode);
                    }
                }
                while (commandCode >= 0);
            }
            catch
            {

            }
            finally
            {
                this.OnDisconnected(this);
            }
        }

        public void SendCommand(byte commandCode)
        {
            if (_tcpClient.Connected == true)
            {
                _networkStream.WriteByte(commandCode);
            }
        }
        

        // Events
        internal event Action<MessagingClient> Disconnected;
        private void OnDisconnected(MessagingClient messagingClient)
        {
            #region Require

            if (messagingClient == null) throw new ArgumentNullException();

            #endregion
            var handler = this.Disconnected;
            if (handler != null)
            {
                SendOrPostCallback handlerDelegate = delegate(object state)
                {
                    handler = this.Disconnected;
                    if (handler != null)
                    {
                        handler(messagingClient);
                    }
                };
                _syncContext.Post(handlerDelegate, null);
            }
        }

        public event Action<byte> CommandArrived;
        private void OnCommandArrived(byte commandCode)
        {
            var handler = this.CommandArrived;
            if (handler != null)
            {
                SendOrPostCallback handlerDelegate = delegate(object state)
                {
                    handler = this.CommandArrived;
                    if (handler != null)
                    {
                        handler(commandCode);
                    }
                };
                _syncContext.Post(handlerDelegate, null);
            }
        }        
    }
}

MessagingListener
-封装TcpListener的对象。
-提供较方便的数据TCP联机管理功能。

namespace DeviceProjectionSample.Concretion
{
    public class MessagingListener : IDisposable
    {
        // Fields
        private readonly SynchronizationContext _syncContext = null;

        private readonly TcpListener _tcpListener = null;

        private readonly Thread _listenThread = null;

        private bool _isDisposed = false;


        private readonly object _syncRoot = new object();

        private readonly List<MessagingClient> _messagingClientList = new List<MessagingClient>();


        // Constructor 
        public MessagingListener(SynchronizationContext syncContext, IPEndPoint localIPEndPoint)
        {
            #region Require

            if (syncContext == null) throw new ArgumentNullException();
            if (localIPEndPoint == null) throw new ArgumentNullException();

            #endregion
            _syncContext = syncContext;
            _tcpListener = new TcpListener(localIPEndPoint);
            _listenThread = new Thread(this.ListenClient);
        }

        public void Start()
        {
            // Start
            _tcpListener.Start();
            _listenThread.Start();
        }

        public void Dispose()
        {
            // Require
            if (_isDisposed == true) return;
            _isDisposed = true;

            // Dispose
            _tcpListener.Stop();
            _listenThread.Join();

            MessagingClient[] messagingClientArray = null;
            lock (_syncRoot)
            {
                messagingClientArray = _messagingClientList.ToArray();
                _messagingClientList.Clear();
            }
            foreach (MessagingClient messagingClient in messagingClientArray)
            {
                messagingClient.Dispose();
            }
        }


        // Methods
        private void ListenClient(object obj)
        {
            try
            {
                while (true)
                {
                    TcpClient tcpClient = _tcpListener.AcceptTcpClient();
                    if (tcpClient != null)
                    {
                        MessagingClient messagingClient = new MessagingClient(_syncContext, tcpClient);
                        lock (_syncRoot)
                        {
                            messagingClient.Disconnected += new Action<MessagingClient>(MessagingClient_Disconnected);
                            _messagingClientList.Add(messagingClient);
                        }
                        this.OnMessagingClientArrived(messagingClient);
                    }
                }
            }
            catch (Exception ex)
            {
                if (_isDisposed == false)
                {
                    Debug.Fail(ex.Message);
                }
            }
            finally
            {
                _tcpListener.Stop();
            }
        }

        public MessagingClient GetMessagingClient(IPEndPoint ipEndPoint)
        {
            #region Require

            if (ipEndPoint == null) throw new ArgumentNullException();

            #endregion
            lock (_syncRoot)
            {
                foreach (MessagingClient messagingClient in _messagingClientList)
                {
                    if (messagingClient.IPEndPoint.ToString() == ipEndPoint.ToString())
                    {
                        return messagingClient;
                    }
                }
                return null;
            }
        }


        // Handlers 
        private void MessagingClient_Disconnected(MessagingClient messagingClient)
        {
            #region Require

            if (messagingClient == null) throw new ArgumentNullException();

            #endregion

            // Remove
            lock (_syncRoot)
            {
                if (_messagingClientList.Contains(messagingClient) == false)
                {
                    return;
                }
                _messagingClientList.Remove(messagingClient);
            }
            
            // Event
            this.OnMessagingClientDeparted(messagingClient);
        }


        // Events
        public event Action<MessagingClient> MessagingClientArrived;
        private void OnMessagingClientArrived(MessagingClient messagingClient)
        {
            var handler = this.MessagingClientArrived;
            if (handler != null)
            {
                SendOrPostCallback handlerDelegate = delegate(object state)
                {
                    handler = this.MessagingClientArrived;
                    if (handler != null)
                    {
                        handler(messagingClient);
                    }
                };
                _syncContext.Post(handlerDelegate, null);
            }
        }

        public event Action<MessagingClient> MessagingClientDeparted;
        private void OnMessagingClientDeparted(MessagingClient messagingClient)
        {
            var handler = this.MessagingClientDeparted;
            if (handler != null)
            {
                SendOrPostCallback handlerDelegate = delegate(object state)
                {
                    handler = this.MessagingClientDeparted;
                    if (handler != null)
                    {
                        handler(messagingClient);
                    }
                };
                _syncContext.Post(handlerDelegate, null);
            }
        }
    }
}

LightDeviceSketch
-ILightDeviceSketch的实做,主要是转接MessagingClient。

namespace DeviceProjectionSample.Concretion
{
    public class LightDeviceSketch : ILightDeviceSketch
    {
        // Constructor 
        public LightDeviceSketch(IPEndPoint ipEndPoint)
        {
            #region Require

            if (ipEndPoint == null) throw new ArgumentNullException();

            #endregion
            this.IPEndPoint = ipEndPoint;
        }


        // Properties
        public IPEndPoint IPEndPoint { get; private set; }


        // Event
        public event PropertyChangedEventHandler PropertyChanged;
        protected void OnPropertyChanged(string propertyName)
        {
            #region Require

            if (string.IsNullOrEmpty(propertyName) == true) throw new ArgumentNullException();

            #endregion
            this.OnPropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
        protected void OnPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            #region Require

            if (sender == null) throw new ArgumentNullException();
            if (e == null) throw new ArgumentNullException();

            #endregion
            var handler = this.PropertyChanged;
            if (handler != null)
            {
                handler(sender, e);
            }
        }
    }
}

LightDeviceControl
-ILightDeviceControl的实做,主要是转接MessagingClient。
-封装了与LightDevice之间沟通的通讯协议。

namespace DeviceProjectionSample.Concretion
{
    public class LightDeviceControl : ILightDeviceControl
    {
        // Fields
        private readonly MessagingClient _messagingClient = null;              

        private bool _lightStatus = false;


        // Constructor 
        public LightDeviceControl(MessagingClient messagingClient)
        {
            #region Require

            if (messagingClient == null) throw new ArgumentNullException();

            #endregion
            _messagingClient = messagingClient;
            _messagingClient.CommandArrived += new Action<byte>(MessagingClient_CommandArrived);
        }        
        
        public void Start()
        {
            _messagingClient.Start();
        }

        public void Dispose()
        {
            _messagingClient.Dispose();
        }


        // Properties
        public bool LightStatus
        {
            get { return _lightStatus; }
            set { _lightStatus = value; this.OnPropertyChanged("LightStatus"); }
        }


        // Methods
        public void OpenLight()
        {
            _messagingClient.SendCommand(0x01);
        }

        public void CloseLight()
        {
            _messagingClient.SendCommand(0x00);
        }


        // Handler 
        private void MessagingClient_CommandArrived(byte commandCode)
        {
            switch (commandCode)
            {
                case 0: if (this.LightStatus != false) { this.LightStatus = false; } break;
                case 1: if (this.LightStatus != true) { this.LightStatus = true; } break;
            }
        }


        // Event
        public event PropertyChangedEventHandler PropertyChanged;
        protected void OnPropertyChanged(string propertyName)
        {
            #region Require

            if (string.IsNullOrEmpty(propertyName) == true) throw new ArgumentNullException();

            #endregion
            this.OnPropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
        protected void OnPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            #region Require

            if (sender == null) throw new ArgumentNullException();
            if (e == null) throw new ArgumentNullException();

            #endregion
            var handler = this.PropertyChanged;
            if (handler != null)
            {
                handler(sender, e);
            }
        }
    }
}

LightDeviceSketchExplorer
-IDeviceSketchExplorer的实做,主要是转接MessagingListener。

namespace DeviceProjectionSample.Concretion
{
    public class LightDeviceSketchExplorer : IDeviceSketchExplorer<ILightDeviceSketch>
    {
        // Fields
        private readonly MessagingListener _messagingListener = null;  


        // Constructor 
        public LightDeviceSketchExplorer(MessagingListener messagingListener)
        {
            #region Require

            if (messagingListener == null) throw new ArgumentNullException();

            #endregion
            _messagingListener = messagingListener;
            _messagingListener.MessagingClientArrived += new Action<MessagingClient>(MessagingListener_MessagingClientArrived);
            _messagingListener.MessagingClientDeparted += new Action<MessagingClient>(MessagingListener_MessagingClientDeparted);
        }         
        
        public void Start()
        {
            _messagingListener.Start();
        }

        public void Dispose()
        {
            _messagingListener.Dispose();
        }


        // Handlers 
        private void MessagingListener_MessagingClientDeparted(MessagingClient messagingClient)
        {
            #region Require

            if (messagingClient == null) throw new ArgumentNullException();

            #endregion
            this.OnDeviceSketchArrived(new LightDeviceSketch(messagingClient.IPEndPoint));
        }

        private void MessagingListener_MessagingClientArrived(MessagingClient messagingClient)
        {

            #region Require

            if (messagingClient == null) throw new ArgumentNullException();

            #endregion
            this.OnDeviceSketchDeparted(new LightDeviceSketch(messagingClient.IPEndPoint));
        }       


        // Events
        public event EventHandler<DeviceSketchNotifiedEventArgs<ILightDeviceSketch>> DeviceSketchArrived;
        private void OnDeviceSketchArrived(ILightDeviceSketch deviceSketch)
        {
            #region Require

            if (deviceSketch == null) throw new ArgumentNullException();

            #endregion
            var handler = this.DeviceSketchArrived;
            if (handler != null)
            {
                handler(this, new DeviceSketchNotifiedEventArgs<ILightDeviceSketch>(deviceSketch));
            }
        }

        public event EventHandler<DeviceSketchNotifiedEventArgs<ILightDeviceSketch>> DeviceSketchDeparted;
        private void OnDeviceSketchDeparted(ILightDeviceSketch deviceSketch)
        {
            #region Require

            if (deviceSketch == null) throw new ArgumentNullException();

            #endregion
            var handler = this.DeviceSketchDeparted;
            if (handler != null)
            {
                handler(this, new DeviceSketchNotifiedEventArgs<ILightDeviceSketch>(deviceSketch));
            }
        }
    }
}

LightDeviceFactory
-IDeviceFactory的实做,主要是在生成LightDevice的时候,将相关的对象做关联。

namespace DeviceProjectionSample.Concretion
{
    public class LightDeviceFactory : IDeviceFactory<LightDevice, ILightDeviceSketch, ILightDeviceControl>
    {
        // Fields
        private readonly MessagingListener _messagingListener = null;  


        // Constructor 
        public LightDeviceFactory(MessagingListener messagingListener)
        {
            #region Require

            if (messagingListener == null) throw new ArgumentNullException();

            #endregion
            _messagingListener = messagingListener;
        }       


        // Methods
        public LightDevice CreateDevice(ILightDeviceSketch deviceSketch)
        {
            #region Require

            if (deviceSketch == null) throw new ArgumentNullException();

            #endregion
            MessagingClient messagingClient = _messagingListener.GetMessagingClient(deviceSketch.IPEndPoint);
            if (messagingClient == null) return null;
            return new LightDevice(deviceSketch, new LightDeviceControl(messagingClient));
        }
    }
}

范例实做 :

最后就只剩下的范例实做。
主要的参与者有:

LightMaster.exe
-提供在WinForm上如何使用实做出来的LightDeviceManager对象。

namespace LightMaster
{
    public partial class Form1 : Form
    {
        // Fields
        private readonly IPEndPoint _localIPEndPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 1234);

        private readonly LightDeviceManager _lightDeviceManager = null;


        // Constructor 
        public Form1()
        {
            // Base
            InitializeComponent();

            // LightDeviceManager
            MessagingListener messagingListener = new MessagingListener(SynchronizationContext.Current, _localIPEndPoint);
            LightDeviceFactory lightDeviceFactory = new LightDeviceFactory(messagingListener);
            LightDeviceSketchExplorer lightDeviceSketchExplorer = new LightDeviceSketchExplorer(messagingListener);
            _lightDeviceManager = new LightDeviceManager(lightDeviceFactory, lightDeviceSketchExplorer);

            // LightDeviceDataGridView
            BindingList<LightDevice> lightDeviceBindingList = new BindingList<LightDevice>();
            _lightDeviceManager.DeviceArrived += delegate(object sender, DeviceNotifiedEventArgs<LightDevice> e) { lightDeviceBindingList.Add(e.Device); };
            _lightDeviceManager.DeviceDeparted += delegate(object sender, DeviceNotifiedEventArgs<LightDevice> e) { lightDeviceBindingList.Remove(e.Device); };
            this.LightDeviceBindingSource.DataSource = lightDeviceBindingList;
            this.LightDeviceDataGridView.Refresh();
        }        

        private void Form1_Load(object sender, EventArgs e)
        {
            // LightDeviceManager
            _lightDeviceManager.Start();
        }

        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            // LightDeviceManager
            _lightDeviceManager.Dispose();
        }


        // Handler 
        private void OpenLightButton_Click(object sender, EventArgs e)
        {
            LightDevice device = this.LightDeviceBindingSource.Current as LightDevice;
            if (device != null) device.OpenLight();
        }

        private void CloseLightButton_Click(object sender, EventArgs e)
        {
            LightDevice device = this.LightDeviceBindingSource.Current as LightDevice;
            if (device != null) device.CloseLight();
        }
    }
}

LightDevice.exe
-是仿真远程设备TCP联机处理、数据收发...等等行为的简单实做。
-相关细节可以下载范例档案参考。

后记 :

类似这样功能明确的Architecture Pattern,其实在大大小小的专案里都有。
花点心思整理纪录,让以后遇到类似需求的时候,能有文件来做沟通跟选择。
也希望让开发人员在需要实做相关功能时,能有一个参考的架构。

原文地址:https://www.cnblogs.com/clark159/p/2335656.html