在编写 python 对象工厂时,我在构造函数中遇到了 lot 参数重复。感觉不对,好像有更好的方法来使用这种模式。我不确定我是否应该用
**kwargs
替换参数,或者是否有更适合这种情况的不同设计模式。
下面是一个简化的例子。真正的代码当然更复杂,你可以看到我这样做的更多原因,但我认为这是一个合理的最小可重现示例
在这些类之外,对于API,最重要的因素是
species
和subspecies
。恰好在内部,is_salt_water
很重要,导致不同的对象,但这是内部问题。
class Fish:
def __init__(self, species, sub_species, length, weight): # Repeating this a lot
self.species = species
self.sub_species = sub_species
self.length = length
self.weight = weight
self.buoyancy = self.buoyancy()
def buoyancy(self):
raise Exception("Do not call this abstract base class directly")
class FreshWaterFish:
def __init__(self, species, sub_species, length, weight): # Repeating this a lot
self.fresh_water = True
super().__init__(species, sub_species, length, weight) # Repeating this a lot
def buoyancy(self):
self.buoyancy = 3.2 * self.weight #totally made-up example. No real-world meaning
class SaltWaterFish:
def __init__(self, species, sub_species, length, weight): # Repeating this a lot
self.fresh_water = False
super().__init__(species, sub_species, length, weight) # Repeating this a lot
def buoyancy(self):
self.buoyancy = 1.25 * self.weight / self.length #totally made-up example. No real-world meaning
def FishFactory(self, species, sub_species, length, weight, is_salt_water = False): # Repeating this a lot
mapper = {True : FreshWaterFish, False: SaltWaterFish}
return mapper[is_salt_water](species, sub_species, length, weight) # Repeating this a lot
诀窍是使用依赖倒置原则。与其提供具体的实现
species
、sub_species
、length
、weight
,不如将它们封装到一个对象中并传递该对象。您几乎已经使用 Fish
类完成了该操作,但如您所见,从 Fish
继承会创建冗余参数列表。
你有一个选择是使用组合来模拟继承,这反过来又满足了依赖倒置原则。在这里,我们将
FreshWaterFish
和 SaltWaterFish
包裹在 Fish
类中。为了保持派生的 fish 类的多态性,我们将抽象方法分离到一个接口中。实际上,我们将行为与数据分开。
class Fish:
def __init__(self, species, sub_species, length, weight):
self.species = species
self.sub_species = sub_species
self.length = length
self.weight = weight
class IFish:
@abstractmethod
def buoyancy(self):
pass
@abstractmethod
def swim(self):
pass
class FreshWaterFish(IFish):
def __init__(self, fish):
self.fish = fish
self.fresh_water = True
self.buoyancy() # May not be best to put behavior methods inside constructors, but just following your example
def buoyancy(self):
self.buoyancy = 3.2 * self.fish.weight
def swim(self):
print('swim swim')
class SaltWaterFish(IFish):
def __init__(self, fish):
self.fish = fish
self.fresh_water = False
self.buoyancy() # May not be best to put behavior methods inside constructors, but just following your example
def buoyancy(self):
self.buoyancy = 1.25 * self.fish.weight / self.fish.length
def swim(self):
print('swam swam')
def FishFactory(self, species, sub_species, length, weight, is_salt_water = False):
mapper = {True : FreshWaterFish, False: SaltWaterFish}
fish_base = Fish(species, sub_species, length, weight)
return mapper[is_salt_water](fish_base)
在某处提供长参数列表仍然是不可避免的,但现在冗余被最小化到有意义的地方并且代码相当可扩展(假设二进制淡水/咸水是语义概括)。
使用继承时,请务必考虑“X 是 Y”关系是否不是最佳方法。是的,它在语义上是有道理的,但这种思考会在您的代码中产生实际问题。
来源:
另见:
我不知道更好的方法,但它们可能是 首先你可以使用 *args, **kwargs 但是如果你想要编译器的帮助如果没有正确完成它会很糟糕
我只是尽量避免完全替换init
在某些情况下最好保存一个类变量(比如在这种情况下 fresh_water 对每个人都是一样的)
class FreshWaterFish:
fresh_water = True
def buoyancy(self):
self.buoyancy = 3.2 * self.weight
或者在另一个函数中获取信息,比如
class Big:
def __init__(self, a, l, o, t, o, f, args):
...
self.init()
def init(self):
pass
class Small(Big):
def init(self):
self.dragon = False
我的第二个选择是使用字典作为所有兼职参数的单个参数,所以我可以很容易地用 kwargs 混淆它,并添加像
这样的描述def function(dictionary):
""":param dictionary: {name:str , age:int, money:FileNotFoundError}"""
...
希望有帮助