zoukankan      html  css  js  c++  java
  • [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,其实在大大小小的专案里都有。
    花点心思整理纪录,让以后遇到类似需求的时候,能有文件来做沟通跟选择。
    也希望让开发人员在需要实做相关功能时,能有一个参考的架构。

  • 相关阅读:
    《Maven实战》第6章 仓库
    Log4j2中的同步日志与异步日志
    CSDN VIP如何添加自定义栏目
    CSDN VIP如何添加引流自定义栏目
    kubernetes垃圾回收器GarbageCollector Controller源码分析(二)
    java图形验证码实现
    java这个404你能解决吗?
    innodb笔记
    java项目启动脚本
    服务器初始化脚本
  • 原文地址:https://www.cnblogs.com/clark159/p/2335656.html
Copyright © 2011-2022 走看看