0

I came across this SESE page: What is a proper use of downcasting? (for C#) -- and it talks about when to downcast, and the possible downsides to it.

In Python sub classes are slightly different

Example:

Suppose I have the following abstract class:

from abc import ABCMeta
from abc import abstractmethod

class Weapon(metaclass=ABCMeta):

    def __init__(self, name, damage):
        self.name = name
        self.damage = damage

    @abstractmethod
    def prepare(self):
        pass

    @abstractmethod
    def attack(self):
        pass

    @abstractmethod
    def cleanup(self):
        pass

    def __str__(self):
        return "Name: {} Damage: {}".format(self.name, self.damage) 

and Subclass:

from Weapon import Weapon

class ChargeGun(Weapon):

    def __init__(self, name, damage):
        super().__init__(name, damage)

    def prepare(self):
        print("Aiming my Gun");

    def attack(self):
        print("Shooting")

    def cleanup(self):
        print("Lowering gun, and turning off laser sight")

    def charge(self):
        print("Charging Gun. Please hold..(elevator music playing)")

In my subclass ChargeGun, I have the def charge(self): method, and I can declare in the main a ChargeGun object and use it, like this:

cg = ChargeGun("Standard ChargeGun",5)
cg.prepare()
cg.charge()

If I were to introduce Sword as a Weapon subclass, the charge(self): would not be implemented, and Sword isn't even aware of it. In Python, there isn't a need to downcast or use a design pattern.

Python isn't Java/C#.

Questions

  1. How should you be thinking when creating sub classes in languages like Python? Should we be thinking from an abstract point of view, and refactor our code?

    Example: def charge(self): could be seen as a way to prepare a ChargeGun so we can remove it. If I had a silencer, that's a a component of a Gun, so maybe it might make sense to have an abstract WeaponComponent class, a silencer and laser sight could be objects of that class and each would override the def prepare(self): method. A Sword could have a gem or stone that gives it extra strike ability, etc.

That's just one way of thinking, I'm not saying it's the best way to refactor it.

  1. Is it okay to take advantage of Python's ability to use the methods of a subclass directly?
  • Think about this from the perspective of whatever is running the game: how does it know what weapon it's supposed to be handling? If you end up having a whole bunch of logic like if isinstance(weapon, ChargeGun): weapon.charge(), I feel like that answers your question. – jonrsharpe Feb 26 '18 at 17:01
  • @jonrsharpe - How then would you declare and use the ChargeGun, if not directly? Or are you saying don't have the charge(self): method? – user297209 Feb 26 '18 at 17:09
  • 2
    I think you need to spend some time considering why you're bothering with an inheritance hierarchy at all. If you're going to directly instantiate and interact with your subclasses with full knowledge of which they are, it's a waste of time. The point of it is to simplify consumers of your instances. – jonrsharpe Feb 26 '18 at 17:13
3

Give me an API that lets me write code like this:

def useWeapon(someWeapon) :
    someWeapon.charge()
    someWeapon.prepare()
    someWeapon.attack()
    someWeapon.cleanup()

useWeapon( ChargeGun("Standard ChargeGun",5) )

And I wont mind if charge() happens instantly or doesn't change the state of the weapon in anyway. I will mind if it blows up with an error or if I have to test if the weapon needs charging. Sure, this means that objects have to have knowledge of messages that seem to have nothing to do with them. But understand that the interface exists for the client that uses the weapon. Not the weapon.

If you let knowledge of what kind of weapon this is leak out suddenly the client has to KNOW what it's talking to. This is the death of polymorphism.

The favorite example is telling pets to speak. I like not having to tell a dog to bark or a duck to quack. I can just say pet.speak() and whatever pet that is figures out what to do. If I need my pet collection to sometimes be quiet I use a cat (otherwise known as the null object pattern). You tell it to speak and it ignores you (cause it's a cat). My cat is carefully designed to quietly accept and ignore every possible message. I can send my cat anywhere and things still work nicely without blowing up. However, my pet rock is another issue. You tell my pet rock to speak and it tears a hole in the fabric of reality by complaining that it doesn't know how to speak. Now sure you can be careful and check for rocks but it's easier to use cats. They're really good at doing nothing.

  • If I understand what you're saying, it means I should be thinking in an abstract point of view, so regardless of which Weapon I get I can call the necessary methods without it blowing up. Python isn't Java and so you can't say Weapon weapon; You would say ChargeGun(). How do you adjust your thinking or design with languages like that? – user297209 Feb 26 '18 at 17:31
  • Python does objects just fine. Not sure what you're getting at. – candied_orange Feb 26 '18 at 17:32
  • What I mean is, in Java I can do Weapon cg = new ChargeGun(); I can then write methods that take the Weapon interface or abstract class as a parameter, and it doesn't know specifically what weapon it is, just that it's going to get a Weapon but in Python I can't declare it the same way as in Java, I have to say cg = ChargeGun() – user297209 Feb 26 '18 at 17:36
  • Are you talking about strong typing? Python has the same problem with pet rocks that java does. Python just waits until run time to complain about it. – candied_orange Feb 26 '18 at 17:37
  • Unless you use instanceof and check it specifically, which is a code smell. – user297209 Feb 26 '18 at 17:39

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy