Frage

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?
War es hilfreich?

Lösung

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.

Lizenziert unter: CC-BY-SA mit Zuschreibung
scroll top