【问题标题】:What to do in the observer design pattern when observers need different parameters to update当观察者需要不同的参数来更新时,观察者设计模式中该怎么做
【发布时间】:2019-02-19 14:04:56
【问题描述】:

考虑一个制造太空船的太空船工厂。当它生产一艘飞船时,它会更新它所居住的星球上的当前资源,并更新飞船存储的状态。因此,宇宙飞船工厂是两个观察者的出版商。即星球上的资源和宇宙飞船的储存。 .使用观察者设计模式时,观察者必须实现发布者可以在发布者想要发布时调用的更新方法。发布者将其观察者列表存储在 Arraylist 中,并通过迭代每个观察者来调用更新,如下所示:

public class SpaceShipFactory implements Publisher{

    private ArrayList<Observer> observers;
    private int mmRequired;
    private int eRequired;
    private int cRequired;
    private int dRequired;
    private int vRequired;

public void notifyObservers() {
    for(Observer obs : observers)
            obs.update(this.mmRequired, this.eRequired, this.cRequired , this.dRequired, this.vRequired );
}
}

问题是,我们要更新的星球资源存储和宇宙飞船存储是不同的。即,一个减少资源的数量,另一个增加宇宙飞船的数量。因此他们的更新方法可能有不同的签名。所以我们不能对两个观察者使用相同的更新方法。因此我们需要一一调用更新,如下伪代码所示:

public void notifyObservers() { 
    for(Observer obs : observers)
        if(obs.equals(PlanetResourceStorage)
                obs.update(this.mmRequired, this.eRequired, this.cRequired , this.dRequired, this.vRequired );
        else if (obs.equals(SpaceShipStorage)
            obs.update(numberOfManifacturedLightFight, numberOfManifacturedHeavyFighter, numberOfFacturedStarDestoyer,NumberOfFacturedBattleShips,NumberofFacturedDeathStars)

}
}

我想知道我们是否应该这样做?也许我们应该创建一个更新方法,它包含发布者想要发布的所有可能值的参数,并且观察者可以使用它们,即使他们不需要知道更新方法的所有参数。例如:

public class SpaceShipFactory implements Publisher{

    private ArrayList<Observer> observers;
    private int metalRequired;
    private int energyRequired;
    private int carbonRequired;
    private int deuteriumRequired;
    private int vibraumRequired;
    private int numberOfStarDestoyerBuilt;

public void notifyObservers() {
    for(Observer obs : observers)
            obs.update(this.metalRequired, this.energyRequired, this.carbonRequired , this.deuteriumRequired, this.vibriumRequired, this.numberOfStarDestoyerBuilt );
}
}

因此在行星资源更新的实际实现中,关于建造船只数量的参数被忽略了。我还发现输入给定更新方法的所有参数非常乏味,我想知道是否有更简单的方法。

例如,行星资源的更新实现是:

public class PlanetResources implements Observer {

    //Amount of resources of each type
    private int metallicMicrolattice;
    private int energy;
    private int carbyne;
    private int deuterium; 
    private int vibranium;

    // The factories to produce the Resources
    private Publisher aMetallicMicrolatticeFactory;
    private Publisher aSolarPlant;
    private Publisher aCarbyneFactory;
    private Publisher aHeavyWaterExtractor;
    private Publisher aVibraniumMine;

    // The Factory to produce mission units
    private Publisher aShipFactory;

    public void update(int mmAmount, int pEAmount, int pCAmount, int pDAmount,
            int pVAmount, int numberOfManifacturedLightFight, int numberOfManifacturedHeavyFighter, int numberOfFacturedStarDestoyer,int numberOfFacturedBattleShips,int numberofFacturedDeathStars) {
        this.metallicMicrolattice = this.metallicMicrolattice + mmAmount;
        this.setCarbyne(this.getCarbyne()+pCAmount);
        this.deuterium = this.deuterium + pDAmount;
        this.energy = this.getEnergy() + pEAmount;
        this.vibranium = this.vibranium + pVAmount;

        // TODO Auto-generated method stub

    }
}

飞船存储的更新方法是:

public class StationedShip implements Observer
{
    private ArrayList<SpaceShip> lightFighters;
    private ArrayList<SpaceShip> heavyFighters;
    private ArrayList<SpaceShip> cruisers;
    private ArrayList<SpaceShip> battleShips;
    private ArrayList<SpaceShip> bombers;
    private ArrayList<SpaceShip> battleCuisers;
    private ArrayList<SpaceShip> starDestoyers;
    private ArrayList<SpaceShip> superStarDestoyers;
    private ArrayList<SpaceShip> deathStars;
    private ArrayList<SpaceShip> espoinageProbes;
    private ArrayList<SpaceShip> IonShooters;

    private ArrayList<SpaceShip> smallTransforters;
    private ArrayList<SpaceShip> largeTransporters;
    private ArrayList<SpaceShip> recyclers;
    private ArrayList<SpaceShip> colonialShips;
    @Override
    public void update(int planetMetallicMicrolattice, int planetEnergy, int planetCarbyne, int planetDeuterium,
            int planetVibranium,numberOfManifacturedLightFight, int numberOfManifacturedHeavyFighter, int numberOfFacturedStarDestoyer,int numberOfFacturedBattleShips,int numberofFacturedDeathStars) {
        //code to add ships to the arraylist of ships

    }

}

【问题讨论】:

  • 更新方法很复杂,一个函数里有这么多参数也不行。它增加了耦合性和复杂性。

标签: java


【解决方案1】:

您似乎错过了观察者模式的要点。这个想法应该是当事件发生时,任何关心它的人都可以得到事件的通知。你得到了那部分。但是该模式也应该使 observable 不必知道除了通知谁之外的任何事情,因此 observable 不需要知道观察者将要对事件做什么。这就是你缺少的部分。

那是什么意思?这意味着当一艘太空船完工时,任何关心此事件的人都会收到通知。通知中发送的数据只是完成的船的句柄。

给定船舶,船舶存储需要查找它关心的细节。也许是大小、质量等。

考虑到这艘船,行星资产负债表需要查找建造该船的资源(可能通过查找设计和相关的材料清单)。

发布的事件(消息)不需要包括任何观察者可能想知道的所有内容 - 它应该只包括可能发生的最具体的指示。这意味着您不说生产了哪种船(例如,“宪法级”),而是说生产的具体船(例如,“NCC-1701”)。然后,您的数据库(或数据结构)必须允许观察者乘坐特定的船(例如,“NCC-1701”)并将其链接到他们处理事件所需的任何信息。

【讨论】:

    【解决方案2】:

    observable 不应被其观察者实现的算法知识所累。

    你的更新方法太复杂了。只需发布一条描述所发生情况的消息,让观察者决定消息的哪一部分与他们相关。

    【讨论】:

      【解决方案3】:

      发布者应该将他所知道的通知观察者。事实上,在开发过程中,这个参数列表可能会发生变化,因此您可以为事件参数创建一种简单的数据对象,其中包含 Publisher 对事件的所有了解。

      Spaceship Factory 可能会发布类似onShipConstructed 的事件,其中包含飞船类型和资源消耗量。矿厂可能会发布事件onResourcesMined 更新发现的采矿矿物,例如:

      public class SpaceShipFactory implements Publisher
      {
          private ArrayList<Observer> observers;
      
          public enum SpaceshipModel
          {
            LightFighter, 
            HeavyFighter, 
            StarDestoyer,
            BattleShip,
            DeathStar
          };
      
          public static class SpaceShipConstructedArgs
          {
              public SpaceshipModel shipModel;
              public int metalConsumed = 0;
              public int energyConsumed = 0;
              public int carbonConsumed = 0;
              //...
          };
      
          public void notifyObservers(SpaceShipConstrctedArgs e) {
              for(Observer obs : observers) { obs.update(e); }
          }
      
          public void someMethod() {
              // ...
              SpaceShipConstructedArgs evt = new SpaceShipConstructedArgs();
              evt.shipModel = SpaceshipModel.LightFighter;
              evt.metalConsumed = 100;
              notifyObservers(evt);
              // ...
          }
      }
      

      然后是 PlanetResources 的观察者:

      public class PlanetResources implements Observer
      {
          //Amount of resources of each type
          private int metallicMicrolattice;
          private int energy;
          private int carbyne;
          private int deuterium; 
          private int vibranium;
      
          //...
      
          public void update(SpaceShipFactory.SpaceShipConstrctedArgs e)
          {
              // update resources, don't case about ship model
              this.metallicMicrolattice -= e.metalConsumed;
              this.energy -= e.energyConsumed;
              // ...
          }
      }    
      

      然后是 StationedShip 的观察员:

      public class StationedShip implements Observer
      {
          //Ships of each type
          private ArrayList<SpaceShip> lightFighters;
          private ArrayList<SpaceShip> heavyFighters;
          //...
      
          public void update(SpaceShipFactory.SpaceShipConstrctedArgs e)
          {
              // create ships in hangar, don't case about resources consumed
              if (evt.shipModel == SpaceShipFactory.SpaceshipModel.LightFighter)
              {
                  lightFighters.add(new SpaceShipLightFighter(...));
              }
              else if (evt.shipModel == SpaceShipFactory.SpaceshipModel.LightFighter)
              {
                  heavyFighters.add(new SpaceShipHeavyFighter(...));
              }
              // ...
          }
      }    
      

      注意: 您可以对所有机库单位使用单一地图,例如

      public HashMap<SpaceshipModel,ArrayList<SpaceShip>> shipsInHangar;
      

      更好: 你可以让你的船类型成为基类 SpaceShip 的子类,比如

      public class SpaceShipLightFighter extends SpaceShip
      

      那么您也可以为机库中的所有船舶使用单个 ArrayList(您可以使用 getFirePower() 等覆盖方法区分船舶,或者在需要确保特定船舶类型时使用 instanceof(不太可取)。

      public ArrayList<SpaceShip> shipsInHangar;
      

      【讨论】:

        猜你喜欢
        • 2012-05-11
        • 2018-05-16
        • 1970-01-01
        • 2010-12-24
        • 1970-01-01
        • 2011-09-25
        • 1970-01-01
        • 2016-02-20
        • 2023-04-10
        相关资源
        最近更新 更多