Home Tutorials Forums Articles Blogs Movies Library Employment Press
Old 08-15-2012, 01:14 PM   #1
bob56
Registered User
 
Join Date: Mar 2012
Posts: 24
Default Base Class Problem

Right i've got 2 types of Ninjas in my game:

BadNinja - base class for several other baddies that inherit via inheritance

GoodNinja - one hero ninja that has several differing properties from that of BadNinja. From 10 properties only 2 are similar to those of BadNinja

Now given that both classes are similar types i thought i would create one base class 'Ninja' and derive the other classes. The problem is that there is hardly any commonality between the properties, the methods in both these classes are named the same however the implementation for EVERY method is different in comparision to the other class.

Abstract classes are not supported in AS3, even if i did implement it then there isn't one method that is concrete for either of the classes, does that matter? I am considering just creating an interface, INinja and forget trying to make a base class, is this the right thing to do?

I guess the underlying question is at what point is a base class justified in a scenario where there is very little likeness between the properties and methods between 2 related classes.

Hope that makes sense.

Thanks.
bob56 is offline   Reply With Quote
Old 08-15-2012, 06:51 PM   #2
marlopax
Senior Member
 
Join Date: Aug 2008
Posts: 340
Default

ActionScript Code:
package {     import flash.display.MovieClip;     import GoodNinja;     import BadNinja;     public class Main extends MovieClip {         public var gn:GoodNinja;         public var bn:BadNinja;         public function Main() {             gn=new GoodNinja();             bn=new BadNinja();             addChild(gn);             addChild(bn);             getProp();         }         private function getProp() {             trace("Common Prop from Good: "+gn.commonPropA);             trace("Common Prop from Good: "+gn.commonPropB);             trace("Common Prop from Bad: "+bn.commonPropA);             trace("Common Prop from Bad: "+bn.commonPropB);             trace("Private Prop from Good: "+gn.privateGoodPropA);             trace("Private Prop from Bad: "+bn.privateBadPropA);         }     } }

ActionScript Code:
package{     import flash.display.MovieClip;         public class Ninja extends MovieClip{         public var commonPropA:int;         public var commonPropB:int;         public function Ninja(){             commonPropA=25;             commonPropB=30;         }     } }

ActionScript Code:
package{     import Ninja;     public class Goodies extends Ninja{         public var privateGoodPropA:int;                 public function Goodies(){             privateGoodPropA=100;         }     } }

ActionScript Code:
package{     import Ninja;         public class Baddies extends Ninja{         public var privateBadPropA:int;                 public function Baddies(){             privateBadPropA=50;         }     } }

ActionScript Code:
package {     import Goodies;     public class GoodNinja extends Goodies {         public function GoodNinja() {                     }     } }

ActionScript Code:
package{     import Baddies;     public class BadNinja extends Baddies{                 public function BadNinja(){                     }     } }




ö
__________________
marlopax
marlopax is offline   Reply With Quote
Old 08-16-2012, 03:19 PM   #3
tadster
tadster
 
tadster's Avatar
 
Join Date: Feb 2009
Location: Texas
Posts: 2,111
Default

I would just go for the interface, keep in mind that interfaces can be made to have properties as well by use of getters and setters.
__________________
www.actiontad.com - ActionScript and JavaScript sitting in a tree...
tadster is offline   Reply With Quote
Old 08-26-2012, 03:02 AM   #4
ASWC
Super Moderator
 
ASWC's Avatar
 
Join Date: Dec 2007
Location: Greenville, SC
Posts: 6,528
Default

It's not always one or the other, I found quite often a cute solution to this problem by using Interface AND a base class. The base class provides some code implementation that are common and some protected method if needed ready for overriding. At this point with some good planning (breaking up functionality so you can override only the needed one) you can end up with a base class running quite a lot of code while the subclass are nicely modifying it to run the way they need. The Interface then comes in handy too to require only the methods that cannot have similar code when it's the case. At the end it's all a matter of having to override or not.
__________________
aswebcreations
Super Duper!
ASWC is offline   Reply With Quote
Old 10-05-2012, 12:59 PM   #5
glantucan
newbe forever!
 
glantucan's Avatar
 
Join Date: Oct 2004
Location: Madrid, Spain
Posts: 88
Default

If they are so different I would forget about inheritance at all. Just abstract what they have in common in a behaviour helper class and use composition, which I personally think it's more elegant in most of the cases and found it to be usually more flexible than inheritance.
__________________
glantucan
glantucan is offline   Reply With Quote
Old 10-05-2012, 04:07 PM   #6
marlopax
Senior Member
 
Join Date: Aug 2008
Posts: 340
Default

Yes you are right. But for forum's interest I'd request you to POST your best expression on it with example.



Regards
__________________
marlopax
marlopax is offline   Reply With Quote
Old 10-05-2012, 06:12 PM   #7
glantucan
newbe forever!
 
glantucan's Avatar
 
Join Date: Oct 2004
Location: Madrid, Spain
Posts: 88
Default

Ok, that's an easy enough challenge.

ActionScript Code:
package ninjas {     public class FightAndHeal     {         private var _hitPoints:int;         private var _health:int;         private var _healPoints         public function FightAndHeal(health:int, hitDamage:int):void{                     _health = health;             _hitDamage = hitDamage;         }                    public function takeDamage(hitDamage:int):void{             _health-=hitDamage;         }                 public function recover():void{             _health += healPoints;         }                 public function attack(target:IFightAble):void {             target.takeDamage(_hitPoints)         }                 public function heal(target:IFightAble):void{             target.heal(healPoints);         }                 internal set hitPoints(value:int):void         {             _hitPoints = value;         }         internal set health(value:int):void         {             _health = value;         }         internal set healPoints(value:int):void         {             _healPoints = value;         }     }     public interface IFightAble     {         public function heal(target:IFightAble):void;         public function attack(target:IFightAble):void;     }     public class NinjaBeGood inplements IFightable     {         private _fighAndHeal:FightAndHeal = new FightAndHeal();                 // any other good only ninja properties go here         // it is supossed to be a lot of stuff                         public function NinjaBeGood():void{             _fightAndHeal.health = 100;             _fightAndHeal.healPoints = 10;             _fightAndHeal.hitPoints = 5;         }                 public function takeDamage(hitDamage:int):void{             _fightAndHeal.takeDamage(hitDamage);         }                 public function recover():void         {             _fightAndHeal.recover();         }                 public function attack(target:IFightAble):void         {             _fightAndHeal.attack(target);         }                 public function heal(target:IFightAble):void{             _fightAndHeal.heal(target);         }                 // any other good only ninja methods go after         // it is supossed to be a lot of stuff     }         public class NinjaBeBad inplements IFightable     {         private _fighAndHeal:FightAndHeal = new FightAndHeal();                 // any other bad only ninja properties go here         // it is supossed to be a lot of stuff                         public function NinjaBeBad():void{             _fightAndHeal.health = 200;             _fightAndHeal.healPoints = 5;             _fightAndHeal.hitPoints = 15;         }                 public function takeDamage(hitDamage:int):void{             _fightAndHeal.takeDamage(hitDamage);         }                 public function recover():void         {             _fightAndHeal.recover();         }                 public function attack(target:IFightAble):void         {             _fightAndHeal.attack(target);         }                 public function heal(target:IFightAble):void{             _fightAndHeal.heal(target);         }                 // any other bad only ninja methods go after         // it is supossed to be a lot of stuff     } }


Inheritance helps make code easier to change if the needed change involves adding a new subclass. This, however, is not the only kind of change you may need to make. If you need to change the superclass interface that can ripple out and break any code that uses the superclass or any of its subclasses. What's more, a change in the superclass interface can break the code that defines any of its subclasses.

Composition doesn't solve that problem completely, but at least helps containing the side effects. Here, if you need to change the interface of
the FightAndHeal class you only need to make minor adjustments in the methods which delegate on it in the front-end classes (NinjaBeGood and NinjaBeBad).

On the other side, it's easier to change the interface of the front-end classes than in subclassses. Here we could change the signature of the recover() and attack() methods in any or either of NinjaBeGood and NinjaBeBad and still be using the commmon behavior of the back-end class. That's what I meant with flexibility.

Further on, composition allows you to delay the creation of back-end objects until (and only if) they are needed, as well as changing the back-end objects dynamically throughout the lifetime of the front-end object (then would be a good idea to define another explicit interface for FightAndHeal and the other classes to switch to).

On the other side, you have to write more code to implement composition, but it's worth it when the common properties and methods are so little.

Hope that answer the question.

P.D.: By the way I did not test the code as I'm off my development environment now. Apoligies in advance for any typo.

Cheers!
__________________
glantucan
glantucan is offline   Reply With Quote
Old 10-09-2012, 09:21 AM   #8
Barna Biro
Senior Member
 
Barna Biro's Avatar
 
Join Date: Nov 2009
Location: LU, Switzerland
Posts: 1,410
Default

I'd approach it a bit differently...

A Ninja class seems enough since in essence, both are Ninja just that depending on their state / configuration, they behave / attack differently. That being said, each Ninja can vary based on the model ( configuration of the model / it's state ) that it references... a NinjaModel would hold information such as "name", "health", "maxHealth", "type" ( either 'good' or 'bad' or whatever... ) and different strategies like 'attackStrategy', 'runStrategy', 'jumpStrategy', etc.

The Ninja class itself would mainly be just a visual representation of the NinjaModel ( good or bad skin based on the ninja type, etc. ) and could expose some methods like attack(target:Ninja):void where this method would in essence be a proxy for the concrete 'attackStrategy' ( the concrete implementation of how the ninja should attack... this strategy could also calculate the damage the ninja attacks with based on, let's say... level, current health, power-ups / if any, etc. ).

Having a possibly huge inheritance chain, IMO is not that cool... GoodNinja, BadNinja, SometimesBadNinja, HalfGoodNinja, NotSureWhatKindOfNinja... you can clearly see this getting out of control quite soon...

glantucan's composition approach at it's core is a nice idea, just that the execution ain't that nice ( IMO ). We in essence have yet again the ugly inheritance chain... we could of course at least define a common Base for the Ninja, since quite a few methods are repeating themselves ( just the initial Ninja configuration differs... but that can be fed from the outside, no need to hardcode it in the constructor of each concrete type ).

Anyway... I've thrown together a fast example... not the neatest possible ( that's why it's "fast" ), but hopefully it's enough to demonstrate the idea.
Attached Files
File Type: zip NinjaExample.zip (8.2 KB, 55 views)
__________________
Titus M. Plautus - Not by age but by capacity is wisdom acquired.

Last edited by Barna Biro; 10-09-2012 at 09:27 AM.
Barna Biro is offline   Reply With Quote
Old 10-09-2012, 11:44 AM   #9
glantucan
newbe forever!
 
glantucan's Avatar
 
Join Date: Oct 2004
Location: Madrid, Spain
Posts: 88
Default

Yep!
You are right. It's not that I would never use inheritance. It's more like I would use it when it really comes handy (thinking in the long term).

And your example is pretty nice. I don't think I would have come up with the same approach, as composition enables so many possibilities...

Anyhow, I just wanted to show how composition works if compared to inheritance. Your example use a lot of advanced design patterns engineering that I wouldn't dare to even try to explain.
Let me see: Iterator, Strategy, Observer, Factory,...
... And I see something strange in your MVC implementation (if you where thinking in MVC at all). In theory the model shouldn't know about the controller strategies, its just the view (Ninja) who should know about them. That's another discussion anyway.

BTW, for those who got curious about what i'm talking about there is a good introductory book for design patterns in AS3: "ActionScript Design 3.0 Patterns" by W. Sanders and C. Cumaranatunge. They have a blog where they go deeper in the subject if you are interested.

Anyhow, it's really nice that you share it
Cheers!
__________________
glantucan
glantucan is offline   Reply With Quote
Old 10-09-2012, 03:35 PM   #10
Barna Biro
Senior Member
 
Barna Biro's Avatar
 
Join Date: Nov 2009
Location: LU, Switzerland
Posts: 1,410
Default

Just for fun, I threw together another fast and quite abstract example ( since the lack of proper requirements )... of course things are far from perfect, it's just a demonstrative example and should be taken as such. Cheers!
Attached Files
File Type: zip NinjaExample-02.zip (13.2 KB, 58 views)
__________________
Titus M. Plautus - Not by age but by capacity is wisdom acquired.

Last edited by Barna Biro; 10-09-2012 at 03:57 PM.
Barna Biro is offline   Reply With Quote
Reply


Thread Tools
Display Modes Rate This Thread
Rate This Thread:

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump


All times are GMT. The time now is 10:29 PM.

///
Follow actionscriptorg on Twitter

 


Powered by vBulletin® Version 3.8.5
Copyright ©2000 - 2014, Jelsoft Enterprises Ltd.
Ad Management plugin by RedTyger
Copyright 2000-2013 ActionScript.org. All Rights Reserved.
Your use of this site is subject to our Privacy Policy and Terms of Use.