3
\$\begingroup\$

My problem is that I have a piece of code logic that I'm having trouble fitting into an OO approach, and I was looking for design patterns so that the following code would no longer rely on the ugly

if class is of type A do X else do Y

In Python, we can say I have three "fighting method" classes, class Fight, class Manoeuvre, and class Dodge.

I also have a control class that takes two inputs, attackMode and defendMode. The inputs will always be objects whose classes extend from or are of one of the above three.

The piece of script causing issue is as follows:

from random import Random
# Following are example "Action" objects which are to be compared
# For results
class Action:
 "Base Action all others inherit from"
 __random = Random()
 def __init__(self):
 self.health = 100 
 @classmethod
 def getSuccess(cls):
 return cls.__random.randint(-2,3)
class Dodge(Action):
 def __init__(self):
 super(Dodge, self).__init__() 
class Attack(Action):
 def __init__(self):
 super(Attack, self).__init__() 
 def performActionOn(self, subject, sucessLevel=0):
 damage = 5 if successLevel > 1 else 3
 subject.health -= damage
class Manoeuvre(Action):
 def __init__(self):
 super(Manoeuvre, self).__init__() 
 def performActionOn(self, subject, successLevel=0):
 pass
# Following is the results configuration
class CombatControl:
 def fight(self, actionAttack, actionDefend):
 if isinstance(actionDefend, Dodge):
 __attackDodge(actionAttack, actionDefend)
 else: 
 __attackAttack(actionAttack, actionDefend) 
 def __attackAttack(self, attack, defend):
 """
 " Defender Attacks back
 """
 resultAttack = attack.getSuccess()
 resultDefend = defend.getSuccess()
 # Both failed. No action.
 if resultAttack < 0 and resultDefend < 0: 
 return
 # Attack wins or draw
 elif resultAttack >= resultDefend:
 attack.performActionOn(defend, successLevel=resultAttack)
 # Defend wins
 else:
 # Does not get damage bonus
 defend.performActionOn(attack)
 def __attackDodge(self, attack, defend):
 """
 " Defender is dodging
 """
 resultAttack = attack.getSuccess()
 resultDefend = defend.getSuccess()
 if resultAttack > resultDefend:
 attack.performActionOn(actionAttack, successLevel=resultAttack)

As you can see in the final case, there's a bit of jiggery-pokery going on where resolution is based upon the type of class. This is undesired.

Ideally I'd want a solution which would allow for something more polymorphic. The structure can change however required if it leads to an elegant solution, as long as the results are the same.

asked Jul 11, 2015 at 16:18
\$\endgroup\$
1
  • \$\begingroup\$ The way you described the situation, multimethod seems to be best fit. Have you taken a look at multimethod (for example, pypi package of the same name)? If you have a lot of situations like that, maybe nonOOP solution like rules engine and rules would fit the bill? \$\endgroup\$ Commented Jul 11, 2015 at 16:25

1 Answer 1

2
\$\begingroup\$

The first thing that came to mind was further encapsulation of the fight mechanics. One thing I did have to change was how success was handled; now it's a member of each action subclass and is given a new value each time Controller.fight is called.

The other part necessary to this approach was the implementation of a action mode (offensive or defensive) so that actions like Dodge would know what to do.

import random
OFFENSIVE = 0
DEFENSIVE = 1
class Action(object):
 def __init__(self):
 super(Action, self).__init__()
 self.health = 100
 self.success = 0
 self.nextSuccess()
 def nextSuccess(self):
 self.success = random.randint(-2, 3)
 def performOn(self, subject, success):
 pass
class Dodge(Action):
 def __init__(self):
 super(Dodge, self).__init__()
 def performOn(self, subject):
 if mode == DEFENSIVE and subject.success > self.success:
 subject.performOn(subject, success=subject.success)
class Attack(Action):
 def __init__(self):
 super(Attack, self).__init__()
 def performOn(self, subject, mode):
 if self.success < 0 and subject.success < 0:
 return
 if mode == OFFENSIVE and self.success >= subject.success:
 subject.health -= 5 if success > 1 else 3
 elif mode == DEFENSIVE and self.success > subject.success:
 subject.health -= 3
class Manoeuvre(Action):
 def __init__(self):
 super(Attack, self).__init__()
class Controller:
 def fight(offense, defense):
 offensive = offense.nextSuccess()
 defensive = defense.nextSuccess()
 offense.performOn(defense, OFFENSIVE)
 defense.performOn(offense, DEFENSIVE)

As far as I can tell this does what the original script did. If there are any tweaks that need to be made, let me know!

Jamal
35.2k13 gold badges134 silver badges238 bronze badges
answered Aug 11, 2015 at 18:47
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.