Is there a better way to do this?

Subscribe to Is there a better way to do this? 4 posts

avatar for Enigmatrix05 Enigmatrix05 42 posts
Flag Post

Encapsulation means that nothing is exposed that does not absolutely need to be exposed, right?

The way my menu navigation system is currently set up, there are MenuItems – which are just little shells that I can stuff graphics data into and make them into uniform width and height. Then there are MenuItemManagers, these hold a MenuItem and add/manager eventListeners (and their associated functions). Then there are MenuManagers, these take MenuItemManagers and build a menu out of them and then append and manage that menu to a given Sprite. Finally I have a MenuDecorator, which you shove a given MenuManager into and staple it to things, as needed.

But I realized it is probably breaking encapsulation to have MenuItemManagers managing what happens when a MenuItem is clicked – because they are then telling the GUI.Artist class what to paint. But if I instead take all of the behaviors I want from my menus (not to mention other graphics) and shove them into Artist… it will become huge.

So I am certain that there is a better ‘pattern’ or design method that I should be using. But being a nub I don’t seem to see one.

Actual code is this. Interfaces:


	public interface AbstractMenuItem 
	{
		function get label() : Sprite
		function set label(v:Sprite) : void
		function get width() : Number
		function set width(v:Number) : void
		function get height() : Number
		function set height(v:Number) : void
	}

	public interface AbstractMenuItemManager 
	{
		function execute() : void
		function get menuItem() : AbstractMenuItem
		function set menuItem(v:AbstractMenuItem) : void
	}

	public interface AbstractMenuManager 
	{
		function apply(v:Sprite) : void
		function hide() : void
		function get menuItemList() : Vector.<AbstractMenuItemManager>
		function set menuItemList(v:Vector.<AbstractMenuItemManager>) : void
		function get spacing() : Number
		function set spacing(v:Number) : void
	}

Classes


	public class ConcreteMenuItem implements AbstractMenuItem 
	{
		private var _label:Sprite;
		private var _width:Number;
		private var _height:Number;
		
		public function ConcreteMenuItem(label:Sprite, width:Number, height:Number) 
		{
			_label = label;
			_width = width;
			_height = height;
			_label.width = _width;
			_label.height = _height;
		}
		
		public static function Blank() : ConcreteMenuItem
		{
			var label:Sprite = new Sprite();
			return new ConcreteMenuItem(label, 30, 20);
		}
		
		/* INTERFACE GUI.managers.AbstractMenuItem */
		
		public function get label():Sprite 
		{
			return _label;
		}
		
		public function set label(value:Sprite):void 
		{
			_label = value;
		}
		
		public function get width():Number 
		{
			return _width;
		}
		
		public function set width(value:Number):void 
		{
			_width = value;
		}
		
		public function get height():Number 
		{
			return _height;
		}
		
		public function set height(value:Number):void 
		{
			_height = value;
		}
		
	}

	public class ConcreteMenuItemManager implements AbstractMenuItemManager 
	{
		private var _menuItem:AbstractMenuItem;
		public function ConcreteMenuItemManager(menuItem:AbstractMenuItem) 
		{
			_menuItem = menuItem;
			_menuItem.label.addEventListener(MouseEvent.CLICK, execute);
		}
		
		public static function Default(menuItem:AbstractMenuItem) : ConcreteMenuItemManager
		{
			return new ConcreteMenuItemManager(menuItem);
		}
		
		
		/* INTERFACE GUI.managers.AbstractMenuItemManager */
		
		public function execute():void 
		{
			// Call to GUI.Artist, tell it to draw a new GUI.schemes.screen.*
		}
		
		public function get menuItem():AbstractMenuItem 
		{
			return _menuItem;
		}
		
		public function set menuItem(value:AbstractMenuItem):void 
		{
			_menuItem = value;
		}
		
	}
	public class DropDownMenuManager implements AbstractMenuManager 
	{
		private var _menuItemList:Vector.<AbstractMenuItemManager>
		private var _spacing:Number;
		public function DropDownMenuManager(menuItemList:Vector.<AbstractMenuItemManager>, spacing:Number) 
		{
			_menuItemList = menuItemList;
			_spacing = spacing;
		}
		
		public static function Empty() : DropDownMenuManager
		{
			var menuItemList:Vector.<AbstractMenuItemManager> = new Vector.<AbstractMenuItemManager>();
			var menuItemManager:AbstractMenuItemManager = ConcreteMenuItemManager.Default(ConcreteMenuItem.Blank());
			return new DropDownMenuManager(menuItemList, 5);
		}
		
		/* INTERFACE GUI.managers.AbstractMenuManager */
		
		public function apply(target:Sprite) : void 
		{
			for (var i:int = 0; i < _menuItemList.length; i ++)
			{
				target.addChild(_menuItemList[i].menuItem.label);
				_menuItemList[i].menuItem.label.x = target.x;
				_menuItemList[i].menuItem.label.y = target.y + (i * _menuItemList[i].menuItem.height) + _spacing;
			}
		}
		
		public function hide():void 
		{
			for (var i:int = 0; i < menuItemList.length ; i++)
			{
				if (_menuItemList[i].menuItem.label.visible == true)
				{
					_menuItemList[i].menuItem.label.visible = false;
				}
				
				else
				{
					_menuItemList[i].menuItem.label.visible = true;
				}
			}
		}
		
		public function get menuItemList():Vector.<AbstractMenuItemManager> 
		{
			return _menuItemList;
		}
		
		public function set menuItemList(value:Vector.<AbstractMenuItemManager>):void 
		{
			_menuItemList = value;
		}
		
		public function get spacing() : Number
		{
			return _spacing;
		}
		
		public function set spacing(v:Number) : void
		{
			_spacing = spacing;
		}
		
	}
	public class MenuDecorator 
	{
		private var _menu:AbstractMenuManager;
		
		public function MenuDecorator(menu:AbstractMenuManager) 
		{
			_menu = menu;
		}
		
		public static function Empty() : MenuDecorator
		{
			return new MenuDecorator(DropDownMenuManager.Empty());
		}
		
		public function apply(target:Sprite) : void
		{
			_menu.apply(target);
		}
	}

Thanks very much for any help or insights you can provide.

 
avatar for stage_phrite stage_phrite 42 posts
Flag Post

You could create some sort of events class which will store any events that happen each frame then in a scene manager class you could check for new events at the beginning of each frame + load/release appropriate scenes based on menu item clicked.

Not sure how well this would work in flash but its how they do it in the quake4 engine.

 
avatar for Enigmatrix05 Enigmatrix05 42 posts
Flag Post

Hm. I see. So build up a queue of events, and then process that queue? How should it behave when two events contraindicate some behavior?

 
avatar for stage_phrite stage_phrite 42 posts
Flag Post

that would really be entirely up to the end user of the class. There are essentially 3 options though….do the first 1 and ignore any others, ignore all but the last or attempt to do them all. It would likely be different for different events so you could possibly add an argument to tell the class what to do in case of conflicts.

if you wanted to handle keypresses with it for example you’d likely want to accept more than 1 keypress event.

you wouldn’t want to load more than 1 scene at once though so you’d have to make sure only 1 of those was processed