Futurescale, Inc. PureMVC Home

The PureMVC Framework Code at the Speed of Thought


Over 10 years of community discussion and knowledge are maintained here as a read-only archive.

New discussions should be taken up in issues on the appropriate projects at https://github.com/PureMVC

Show Posts

* | |

  Show Posts
Pages: [1]
1  Announcements and General Discussion / Architecture / Re: Arch considerations: the Model, Remote Service calls, Proxy, lots of DTOs on: July 24, 2008, 05:57:02
I think it's really up to you.

In PureMVC it is totally fine to place your service logic in the Model layer. It really simplifies many things and speeds up development in many projects.

But recently I've been taking a semi-Cairngorm approach and delegating service responsibility to the Controller layer, which I believe is also allowed in PureMVC (I just don't know if its documented). The controller would retrieve data from the server and update the model/domain layer with the data retrieved from a service. I have a Proxy for each DTO (or Value Object), which is fine cause I see a Proxy as a sort of 'Manager' for a particular DTO. For example, the following UserProxy is responsible for storing a list of UserVOs and provides methods for retrieving specific UserVOs and methods for modifying them.

UserVO:
    name:String
    id:int
 

UserProxy:
    private _persistWithServerMap:Dictionary;

    storeUsers(userList:Array):void
        this.data = userList;

    getUserByName(name:String):UserVO

    getUserById(id:int):UserVO

    updateUser(modifiedUser:UserVO):void
        var user:UserVO = getUserById(modifiedUser.id);
        user.id = modifiedUser.id;
        user.name = modifiedUser.name;
        this._persistWithServerMap[user] = true;

    //A Command would read the contents of this Dictionary to update the server
    //with only the UserVOs that were modified
    get persistWithServerMap():Dictionary
        return this._persistWithServerMap;


If you find that you don't need a Proxy for each DTO, I guess you could simply create a Proxy for storing groups of DTO types.
2  PureMVC Manifold / Bug Report / Re: [WILL FIX] notifyObservers 'lose' data on: July 20, 2008, 01:59:58
Ok folks.

Looking at these topics merged, we have 3 proposed solutions that should work:

#3 looks like the right approach to me.

-=Cliff>

But this comes back to the 2nd point I made. Solution #3 looks like it will only work when you have a Mediator removing itself. What about a situation where a Mediator is removing Mediators further down in the observers array? The Mediator will get removed from the original observers array, but will continue to exist in its copy, which will obviously be disastrous once the second loop reaches those Mediators that are no longer supposed to exist.
3  PureMVC Manifold / Bug Report / Re: bug in notifyObservers when observers self-destruct on: July 18, 2008, 02:30:35
I came across this very same problem a while back while working on a shooter game. My solution was a bit nastier though.

Although this is so much more simpler and cleaner, I have 2 issues with it:

1. It notifies all registered observers for a particular Notification name in reverse order which is something that I can not have since I've come to rely on the order in which observers are registered in most of my projects. I'm not sure if that's something the framework was designed to guarantee, but I definitely need it to stay that way.

2. It seems to only work in situations where the observer removes itself (only one observer is removed each iteration). What if you have, say a parent Mediator removing MULTIPLE child Mediators registered to the same Notification name as the parent. You'll end up skipping Observers as well.

But again, your solution looks like it definitely works for situations in which the current observer removes itself.


The following should fix the problem when removing multiple observers registered to the same notification name (see below for version with comments)


public function notifyObservers( notification:INotification ):void {

   if ( observerMap[ notification.getName() ] != null ) {
      var observers:Array = observerMap[ notification.getName() ] as Array;
               
        var prevLength:int;
        var removedObservers:int = 0;
        var traversedList:Array = new Array();
               

      for (var i:Number = 0; i < observers.length; i++) {
         var observer:IObserver = observers[ i ] as IObserver;


           traversedList.push(observer);
           prevLength = observers.length;

         observer.notifyObserver( notification );


           if (observers.length < prevLength) {
            
              for (var j:int = 0; j < traversedList.length; j++) {
               
                 if (observers.indexOf(traversedList[j]) == -1) {
                    traversedList.splice(j, 1);
                    j--;

                    removedObservers++;
                 }
              }
           }

           i -= removedObservers;
           removedObservers = 0;

      }
   }
}


//WITH COMMENTS

public function notifyObservers( notification:INotification ):void {

   if ( observerMap[ notification.getName() ] != null ) {
      var observers:Array = observerMap[ notification.getName() ] as Array;
      var prevLength:int;
      var removedObservers:int = 0;
      var traversedList:Array = new Array();
      for (var i:Number = 0; i < observers.length; i++) {
         var observer:IObserver = observers[ i ] as IObserver;

         traversedList.push(observer);
         prevLength = observers.length;

         observer.notifyObserver( notification );

         //If observers.length has changed during execution of the observer
         //then observer(s) were removed from the list and we must move back
         //the i counter so that we resume with the observer after the observer
         //we left off at.
         //Counter will be rolled back only when observers before the current
         //observer are removed (ex. i is at 4 and observers at i=0 and i=1 are removed)
         if (observers.length < prevLength) {
            //traversedList holds list of observers we've already traversed through
            for (var j:int = 0; j < traversedList.length; j++) {
               //Determine the number of observers that were removed by checking
               //if any of the observers in the traversedList no longer exist in
               //the observers list
               if (observers.indexOf(traversedList[j]) == -1) {
                  traversedList.splice(j, 1);
                  j--;

                  removedObservers++;
               }
            }
         }
         //rollback i
         i -= removedObservers;
         removedObservers = 0;
      }
   }
}
4  Announcements and General Discussion / General Discussion / Re: Game Logic Placement (Command or Proxy) on: November 28, 2007, 02:31:32
Interesting. I'll just leave the logic in the model for repackaging purposes then. But then that means that the Command is simply there solely for calling methods on the Proxy.

class ShipHitCommand {
  execute() {
     stateProxy.playerIsHit()
  }
}

So the Command is instantiated every time the player is hit simply to call a method on the Proxy. It seems like it would make more sense for me to call the Proxy method directly from the ShipMediator:

class ShipMediator {
  onHitTest(Ship, laser.x, laser,y) {
     stateProxy.playerIsHit();
  }
}

That way I skip the Command instantiating, but at the same time I introduce tight coupling between the Mediator and the Proxy. I understand that Commands are there in order to prevent coupling between the View and Model, but it seems like I'm going to have constant instantiating of Commands every time a player's ship is hit, or an enemy it hit, or an obstacle hits the player, etc. So I would have a Command for every possible event.

ShipHitCommand //calls the stateProxy.shipIsHit() method
EnemyHitCommand
ShipHitByRockCommand  //updates ship health in StateProxy
PlayerKeyPressedCommand //updates Ship position in StateProxy
QuitKeyPressedCommand //calls the stateProxy.quit() method
PauseKeyPressedCommand //calls the stateProxy.pause() method

The PureMVC demos on this site seem to all have only one or two commands used soley for instantiating the Proxy and Mediator classes. So I have no clue whether, for games, I need to use many Commands or if I should just call all Proxy methods from the Mediators, that way I skip the Command instantiation process thus eliminating overhead.
5  Announcements and General Discussion / General Discussion / Game Logic Placement (Command or Proxy) on: November 27, 2007, 03:10:37
Hi,

Before I begin I'd just like to say that PureMVC is simply amazing. For my first framework I have to say that it was very easy to pick up and dive into in a matter of days. Simple yet powerful. It has definitely made Flash development fun for me again and not some messy nightmare. And thanks to this framework, my first venture into the Flash development industry as an independent contractor has been a wonderful experience and it's only been my first month. Thank you very much Cliff for this beauty. I hope in to coming weeks, months, and years to give back to the community and contribute some of the stuff I'm able to build using this framework with all you developers out there.

Now onto my question.

I've just about completed my first Flash game using PureMVC and am now working on an online multiplayer Flash game using sockets. Throughout the development process something has been bugging me. I found myself placing most of my game logic inside a Proxy and I'm not so sure that that code is supposed to go in the Proxy. So for example, say a player's ship is hit by enemy fire. I'm going to want to decline the ships health amount by 1. The ships health is stored in a proxy called GameStateProxy which stores various info about the state of the player's ship (lives, health, ammo, etc). So the ships health starts at say, 10, and when it reaches 0 it's game over.

Now here's what takes place in the code. The Mediator sends the Notification(SHIP_HIT) to inform ShipHitCommand that the ship sprite has been hit. It is at that point, in ShipHitCommand's execute method, that I become unsure. Should I make a call to some method on GameStateProxy such as shipIsHit() which decrements the health value by one and checks if it has reached 0:
:
class GameStateProxy extends Proxy implements IProxy {
  private var state:Object;
  public function GameStateProxy() {
    state.health = 10;
  }
  //shipIsHit() called by ShitHitCommand
  public function shipIsHit() {
    health--;
    if (health <= 0) sendNotification(GFacade.GAME_OVER);
  }
  public function get health():Number {
    return state.health;
  }
  public function set health(val:Number):void {
    state.health = val;
  }
}


Or should ShipHitCommand handle that logic:

:
class ShipHitCommand extends SimpleCommand implements ICommand {
  override public function execute(note:INotification) {
     var gameStateProxy = facade.retrieve(GameStateProxy.NAME) as GameStateProxy;
     gameStateProxy.health--;
     if (gameStateProxy.health < 0) sendNotification(GAME_OVER);
  }
}

I reread the "Best Practices" doc and I get the feeling that no logic should go in the Proxy. The Proxy is there only to hold data and should not be reponsible for say, determining if it is game over, that should be the Command's responsibility.

-Andres
Pages: [1]