在从抽象基类派生的一组类中添加功能的正确方法是什么?
设置
我的朋友告诉我,在OOP中,您通常不想修改现有代码库中的任何抽象基类,因为这意味着您必须对每个派生的类实施新的更改。我对使用Pythonic的方式更喜欢对代码库进行的修改感兴趣。重点是更改现有代码库。
示例场景
我有一个抽象基类,称为Animal
,因为使用此库的代码必须与Animal
对象进行交互。 我有多个儿童类实现:dog
和cat
每个都有不同的字段,它们每个字段都需要自己的内部功能。因此,在这一点上,代码库看起来像:
from abc import ABC, abstractmethod
class Animal(ABC):
@abstractmethod
def feed(self, foo: str) -> None:
raise NotImplementedError
class Dog(Animal):
def __init__(self):
self.woof = "woof"
def feed(self, food: str):
print(f"Dog is being fed {food}. It says {self.woof}")
class Cat(Animal):
def __init__(self):
self.meow = "meow"
self.purr = "purr"
def feed(self, food: str):
print(f"Cat is being fed {food}. It says {self.meow}")
实现此操作后的修改
,开发人员意识到他们想从Animal
对象登录相关字段(或状态),以及记录的数据是什么因素而变化。儿童课程的儿童课。
选项A
最初,我的想法是实现另一个AbstractMethod
并以这种方式添加功能。这迫使每个动物>
以任何方式实现新的get_fields()
。
class Animal(ABC):
@abstractmethod
def feed(self, foo: str) -> None:
raise NotImplementedError
@abstractmethod
def get_fields(self) -> list:
raise NotImplementedError
class Dog(Animal):
def __init__(self):
self.woof = "woof"
def feed(self, food: str):
print(f"Dog is being fed {food}. It says {self.woof}")
def get_fields(self) -> list:
return [self.woof]
class Cat(Animal):
def __init__(self):
self.meow = "meow"
self.purr = "purr"
def feed(self, food: str):
print(f"Cat is being fed {food}. It says {self.meow}")
def get_fields(self) -> list:
return [self.meow, self.purr]
选项B
我的朋友说我们不应该修改抽象类,但是,我们提出的唯一其他选择是要做以下操作:
def get_field(animal: Animal) -> list:
if isinstance(animal, Dog):
return [animal.woof]
elif isinstance(animal, Cat):
return [animal.meow, animal.purr]
else:
raise TypeError
您会选择哪一个?还有另一种更好的方法吗?哪一个是更多的Pythonic?
Setting
My friend told me that in OOP, you generally don't want to modify any abstract base classes in an existing codebase, because that means you have to implement the new changes to each and every derived class. I'm interested in which modification to the codebase one would prefer in a preferably pythonic way. The emphasis is on changing an existing codebase.
Example scenario
I have an abstract base class called Animal
, because the code that uses this library has to interact with Animal
objects.
I have multiple child class implementations: Dog
and Cat
that each have a different set of fields that they each need for their own inner functionality. So at this point the code base looks like:
from abc import ABC, abstractmethod
class Animal(ABC):
@abstractmethod
def feed(self, foo: str) -> None:
raise NotImplementedError
class Dog(Animal):
def __init__(self):
self.woof = "woof"
def feed(self, food: str):
print(f"Dog is being fed {food}. It says {self.woof}")
class Cat(Animal):
def __init__(self):
self.meow = "meow"
self.purr = "purr"
def feed(self, food: str):
print(f"Cat is being fed {food}. It says {self.meow}")
Modification
AFTER this is implemented, the developers realize that they want to log the relevant fields (or states) from an Animal
objects and what the logged data is varies from child class to child class.
Option A
Originally, my idea would be to implement another abstractmethod
and add functionality that way. This forces every Animal
to implement the new get_fields()
in whatever way they need to.
class Animal(ABC):
@abstractmethod
def feed(self, foo: str) -> None:
raise NotImplementedError
@abstractmethod
def get_fields(self) -> list:
raise NotImplementedError
class Dog(Animal):
def __init__(self):
self.woof = "woof"
def feed(self, food: str):
print(f"Dog is being fed {food}. It says {self.woof}")
def get_fields(self) -> list:
return [self.woof]
class Cat(Animal):
def __init__(self):
self.meow = "meow"
self.purr = "purr"
def feed(self, food: str):
print(f"Cat is being fed {food}. It says {self.meow}")
def get_fields(self) -> list:
return [self.meow, self.purr]
Option B
My friend is saying we shouldn't modify the abstract class however, the only other option we came up with is to do the following:
def get_field(animal: Animal) -> list:
if isinstance(animal, Dog):
return [animal.woof]
elif isinstance(animal, Cat):
return [animal.meow, animal.purr]
else:
raise TypeError
Which one would you go with? Is there another, better way to do this? Which one is more pythonic?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
在ABC上实现一种通用机制,作为具体方法,但将配置转移到子类,并且不使用硬编码名称。
我在此处使用了
meta
,因为这是您在Django模型中看到的东西的类型,在嵌入式meta 中,name-stacs name-stacs name-stacs in name-stacs in name-spacs in name-space /代码>类。 django专门使用非常相似的系统哪些字段显示用于数据输入的自动生成管理面板中的位置。
输出:
此外,就抽象与混凝土而言,它有助于创造性地思考以保持方法行为统一(并因此是通用),而不是过于挑剔。例如,要么原始设计模式书或一本带有一本书是在谈论复合图案,该图案涉及“树”。好吧,他们所说的是,当您在叶子上(没有孩子)并试图迭代其不存在的孩子时,他们可以返回一个空名单,而不是抛出例外。
Implement a generic mechanism on the ABC, as a concrete method, but devolve the configuration to the subclasses and DONT use hardcoded names.
I've used
Meta
here because that is the type of stuff you see in Django models, name-spacing what is a given class's configuration in an embeddedMeta
class. Django specifically uses a very similar system to track which fields get display where in the auto-generated admin panels used for data entry.output:
Also, in terms of abstract vs concrete, it helps to think creatively to keep method behavior uniform (and therefore generic), rather than being overly picky. For example, either the original Design Patterns book or one taking it up was talking the Composite Pattern, which deals with "trees". Well, what they said was that instead of throwing an exception when you are on a Leaf (no children) and trying to iterate its, non-existent, children, they could just return an empty list.