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

  • 相关阅读:
    JeePlus:代码生成器
    JeePlus:API工具
    Java实现 洛谷 P1023 税收与补贴问题
    Java实现 洛谷 P1023 税收与补贴问题
    Java实现 洛谷 P1023 税收与补贴问题
    Java实现 洛谷 P1328 生活大爆炸版石头剪刀布
    Java实现 洛谷 P1328 生活大爆炸版石头剪刀布
    Java实现 洛谷 P1328 生活大爆炸版石头剪刀布
    Java实现 洛谷 P1328 生活大爆炸版石头剪刀布
    Java实现 洛谷 P1328 生活大爆炸版石头剪刀布
  • 原文地址:https://www.cnblogs.com/clark159/p/2335656.html
Copyright © 2011-2022 走看看