• 设计模式观察者模式


         观察者模式允许我们观察应用程序中一个对象的状态。最好的理解它的方式就是 Publish/ Subscribe 模式。
    在观察者模式中有被观察的叫subject,一些观察subject 的对象叫观察者。该模式是松耦合的一个非常好的实例
    因为我们的类可以依赖很少的信息进行交互。

    subject 需要被三件事关注:
    1. 注册一个观察者
    2. 删除一个观察者
    3. 通知事件的观察者

          比方说我们准备创建一个video game, 在我们的游戏中,我们有一些非玩家角色,这些角色依赖于玩家。
    为了该需要,我们假设我们有一个弓箭手类和一个击剑手类,如果玩家在范围内,弓箭手类能够进行攻击,
    如果玩家已经被关闭,那么击剑手类就能够进行攻击。那么,我们如何应用观察者模式实现这样的控制呢。

          这是一个很好的应用观察者模式的例子。 在这里 玩家就是 subject .  玩家没有任何概念关于有多少非玩家角色在
    观察它。有意思是这都没有关系,因为我们正在广播玩家的位置给关心玩家的非玩家角色。
    这里我们采用的是 2维模式。因为计算两点间的距离比较容易。

          为了实现观察者模式,我们先创建两个接口,第一个为subject.  Subject 需要能够注册观,删除,通知 观察者。
    在这个实例中,观察者要基于subject (玩家) 的位置去尝试进行攻击。下面是定义的接口。

        namespace Observer
        
    {
            
    public interface ISubject
            
    {
                
    void RegisterObserver(IObserver observer);
                
    void UnregisterObserver(IObserver observer);
                
    void NofifyObservers();
            }


            
    public interface IObserver
            
    {
                
    void Attack(ref Position p);
            }

        }

          我们需要创建一个帮助类来表示角色的位置. 这个类也包含两个方法来计算两个点间的距离。在这个类里的每个事
    都应该能被自解释的... 即使你已经对几何生疏了,你也必须相信我正确的计算了两点间的距离。:)


     namespace Observer
       
    {
         
    public class Position
           
    {
              
    private int _x;
              
    private int _y;
         
              
    public int X
              
    {
                
    get return _x; }
                
    set { _x = value; }
             }

             
    public int Y
             
    {
                
    get return _y; }
                
    set { _y = value; }
             }

      
             
    public Position()
             
    {
                _x 
    = _y = 0;
             }

             
    public Position(int x, int y)
             
    {
                _x 
    = x;
                _y 
    = y;
             }

        
             
    // This could be done on 1 line, but I broke
             
    // it apart for readability.
             public double CalcDistance(Position pos)
             
    {
                
    int xsquared = (this.X - pos.X) * (this.X - pos.X);
                
    int ysquared = (this.Y - pos.Y) * (this.Y - pos.Y);
                
    int sum = xsquared + ysquared;
                
    return Math.Sqrt(Convert.ToDouble(sum));
             }

        
             
    public override string ToString()
             
    {
                
    return _x.ToString() + “, “ + _y.ToString();
             }

          }

       }


         我们要把规则简化,我们要有两个类,射箭手类和击剑手类。我们假设一个弓箭手只能在距离
    玩家四到十个单元的范围内攻击玩家。一个击剑手必须在两个单元格内攻击玩家。我们创建一个
    玩家,它必须实作ISubject 接口,因为它要做为 subject . 玩家也要有一个属性表明我们当前
    的位置.

     namespace Observer
        
    {
           
    public class Player : ISubject
          
    {
              
    private List<IObserver> _observers;
              
    private Position _currentPosition;
              
              
    public Position CurrentPosition
              
    {
                
    get return _currentPosition; }
                
    set { _currentPosition = value; }
             }

             
    public List<IObserver> Observers
             
    {
                
    get return _observers; }
             }

        
             
    // We initialize our observer list in the constructor.
             public Player()
             
    {
                _observers 
    = new List<IObserver>();
             }

        
             
    // This is the key event in our scenario.  The only thing
             
    // that the observers care about is the position of the 
             
    // player, so we notify them when it changes.
             public void SetPosition(Position pos)
             
    {
                _currentPosition 
    = pos;
                Console.WriteLine(“Player 
    is at “ + pos.ToString());
                NofifyObservers();
             }

        
             
    // This is our function to register a new observer.
             public void RegisterObserver(IObserver observer)
             
    {
                
    if (!_observers.Contains(observer))
                   _observers.Add(observer);
             }

        
             
    // This takes an observer out of the list.
             public void UnregisterObserver(IObserver observer)
             
    {
                
    if (_observers.Contains(observer))
                   _observers.Remove(observer);
             }

        
             
    // This does the notification.  In our case
             
    // that means each observer will try to attack
             
    // based on the position of the player.
             public void NofifyObservers()
             
    {
                
    foreach (IObserver obs in _observers)
                   obs.Attack(
    ref _currentPosition);
             }

          }

       }


          我们已经建立subject . 现在我们需要实现几个观察者,注意到,RegisterObserver  方法和
    UnregisterObserver  方法 只是简单的从有效的观察者内部列表里添加和删除观察者。这些
    方法都传递一个IObserver 参数,这意味着它们会接受任何实现IObserver  接口的对象。NotifyObserver
    方法简单的轮循观察者列表,并且调用每一个对象的Attack 方法。我们知道每一个观察者都有
    这样的方法,因为在IObserver 接口内被指定了。


          之前提到过,我们实现了两个观察者为这个实例,弓箭手类和击剑手类,下面是它们的实现:

     namespace Observer
        
    {
           
    public class Archer : IObserver
           
    {
              
    private string _name;
              
    private Position _current;
              
    public string Name
              
    {
                 
    get return _name; }
                
    set { _name = value; }
             }

             
    public Position Current
             
    {
                
    get return _current; }
                
    set { _current = value; }
             }

        
             
    public Archer(string name)
             
    {
                _name 
    = name;
                _current 
    = new Position(00);
             }

        
             
    public void SetPosition(Position pos)
             
    {
                _current 
    = pos;
             }

        
             
    public void Attack(ref Position p)
             
    {
                
    // Archer can attack if use is between 4 and 10
                
    // units away.
                double distance = p.CalcDistance(_current);
                
    string formatted = String.Format(“{0:0.00}”, distance); 
                
    if (distance >= 4 && distance <= 10)
                   Console.WriteLine(_name 
                      
    + ” is attacking! [” + formatted + “]”);
                
    else if (distance < 4)
                   Console.WriteLine(_name 
                      
    + ” is too close to attack! [” + formatted + “]”);
                
    else if (distance > 10)
                   Console.WriteLine(_name 
                      
    + ” is too far away to attack! [” + formatted + “]”);
             }

          }

        
          
    public class Swordsman : IObserver
          
    {
             
    private string _name;
             
    private Position _current;
             
    public string Name
             
    {
                
    get return _name; }
                
    set { _name = value; }
             }

             
    public Position Current
             
    {
                
    get return _current; }
                
    set { _current = value; }
             }

        
             
    public Swordsman(string name)
             
    {
                _name 
    = name;
                _current 
    = new Position(00);
             }

        
             
    public void SetPosition(Position pos)
             
    {
                _current 
    = pos;
             }

        
             
    public void Attack(ref Position p)
             
    {
                
    // Swordsman can attack if the distance is 2 
                
    // units or less from the player.
                double distance = p.CalcDistance(_current);
                
    string formatted = String.Format(“{0:0.00}”, distance); 
                
    if (distance <= 2)
                   Console.WriteLine(_name 
                      
    + ” is attacking! [” + formatted + “]”);
                
    else
                   Console.WriteLine(_name 
                      
    + ” is too far away to attack! [” + formatted + “]”);
             }

          }

       }



          现在我们已经准备好了所有的片段,把它们放到一起进行测试。我们的引擎
    是要不断跟踪玩家,并且我们添加两个弓箭手和两个击剑手作为观察者。为了
    更好的观察,我们用下面图形表示每一个成员的起始点。

    我们的引擎是一个简单的控制台程序,这是我添加了两个观察者和玩家后的代码:

       namespace Observer
        
    {
           
    class Program
           
    {
              
    static void Main(string[] args)
              
    {
                 
    // Set up our observers.
                 Archer archer1 = new Archer(“Robin Hood”);
                 archer1.SetPosition(
    new Position(13));
        
                Archer archer2 
    = new Archer(“Legolas”);
                archer2.SetPosition(
    new Position(17));
        
                Swordsman swordsman1 
    = new Swordsman(“Conan”);
                swordsman1.SetPosition(
    new Position(74));
                
                Swordsman swordsman2 
    = new Swordsman(“Aragorn”);
                swordsman2.SetPosition(
    new Position(76));
        
                
    // Set up the subject and register 
                
    // the observers we created.
                Player p = new Player();
                p.RegisterObserver(archer1);
                p.RegisterObserver(archer2);
                p.RegisterObserver(swordsman1);
                p.RegisterObserver(swordsman2);
        
                
    // Set the position of the player.
                
    // This notifies all the observers
                
    // and they will try to attack.
                p.SetPosition(new Position(115));
        
                Console.ReadLine();
             }

          }

       }


     上面的代码 p.SetPosition(new Position(11, 5)); 会广播玩家的位置给观察者,观察者会尝试攻击运行程序会产生下面结果:


    上面的玩家离的太远,我们调整一下玩家的位置
     1: p.SetPosition(new Position(9, 5));


      1: p.UnregisterObserver(archer1);  取消archer1观察者身份

      2: p.SetPosition(new Position(9, 5));


    archer1 不再响应玩家的动作,新的观察者很容易通过RegisterObserver() 方法添加
    松耦合允许我们创建任何种类的观察者,而玩家类不需要知道它。玩家只关心我们创建
    的IObserver接口的观察者实现。

    希望本文能让你对观察者模式有一个清晰的认识。

  • 相关阅读:
    mac下安装oracle客户端
    Mac下安装tomcat
    eclipse上Git使用
    深拷贝1兼容多构造函数
    Oracle查询表大小,更新或查询太慢的时候
    Redis弱密码修改
    学习参考
    c#简单类型转换As<T>
    NPM相关
    java项目和npm项目命令窗口部署方便调试
  • 原文地址:https://www.cnblogs.com/flyinthesky/p/1216530.html
Copyright © 2020-2023  润新知