上下文
我正在用Python创建一个游戏,使用pygame来应用我自己研究的SOLID原理和MVC概念。
我曾经有一个类,我认为,它有不止一个责任(通过改变位置移动,改变移动角度/方向,对用户输入做出反应,开火,损坏实体等)
- 静态
- 可移动
- 可控
- 射击运动员
- 可破坏的
- 射弹
我把它们像接口一样分开,这样不同种类的类都可以使用其中的任何一个(例如,障碍物类是静态的,子弹类是可移动的和可投射的,玩家类是可控的和射手,等等)
前三个界面对我来说很奇怪。基本上:
- Static负责在窗口区域中定位实体
- Movable负责定义移动方向(有角度和移动速度)
- 可控负责对用户输入做出反应(何时移动、开火等)
问题
上面的3个类具有这种依赖关系:
Static <---extends--- Movable <---extends--- Controllable
以下是3个基类的代码以及它们如何相互作用:
class Static(object):
def __init__(self,
pos: Vector2 = None,
off: Vector2 = None):
self._position = pos
self._offset = off
# has some setters and getters
...
class Movable(Static):
def __init__(self,
angle: Vector2 = None,
mov_spd: int = 0,
**static_properties):
super(Movable, self).__init__(**static_properties)
self._angle_vector = angle
self._move_speed = mov_spd
def update_position(self, *args, **kwargs):
raise NotImplementedError
def update_angle(self, *args, **kwargs):
raise NotImplementedError
class Controllable(Movable):
def __init__(self,
move_state: Dict[Enum, bool] = None,
**movable_properties):
super(Controllable, self).__init__(**movable_properties)
# self._move_state = { LEFT: False, RIGHT: False, UP: False, DOWN: False }
self._move_state = move_state
def set_move_state(self, direction):
self._move_state[direction] = not self._move_state[direction]
# overrides and defines Movable method
def update_position(self):
...
# overrides and defines Movable method
def update_angle(self, direction, magnitude):
...
分离的原因是,游戏中有/将有类似小工具的实体,或游戏障碍物(因此实现静态),只是像子弹一样移动的实体(因此实现可移动),或可以由玩家控制的实体(从而实现可控)。
但依赖性的原因是可控性将定义移动的方向,可移动将在其中使用,但需要知道更新它的位置,静态将在其中提供。
问题(2)
- 在这种情况下,类的分解/分离是否合理?如果没有,它们应该有多小/多大
- 这种情况下的依赖关系合理吗?如果没有,如何改进/耦合/解耦它们
初始解决方案:
我确实熟悉了继承与组合,知道从基类继承/组合什么时候好(什么时候坏),但我没有其他方法知道我是否在我的情况下正确使用了它。此外,我仍然不理解"is a"one_answers"has a"的概念,因为它似乎不适用于我试图为实体构建基类/接口的情况。但感觉它应该适用。
我非常了解单一责任原则,知道定位、移动和控制应该在一个单独的类中处理,而不是在一个大类中处理(除非我在定义这些责任时出错)。但现在我想知道定位、移动和控制责任应该有多小/多大
我在9年前刚刚发现了这个StackOverflow问题,并在一定程度上回答了我的第二个问题和个人担忧。但我想知道除了mixin之外,是否还有其他有用的设计可以解决我的问题。
一般来说,您的方法似乎是合理的,但我不明白为什么在Controllable
类中需要重写Movable
类的update_position
和update_angle
?
对我来说,在Controllable
类中实现新方法更有意义,这些方法注册并响应用户的输入,然后使用基类(Movable
)的方法更新位置。所以我会做一些类似的事情:
class Controllable(Movable):
def __init__(self,
move_state: Dict[Enum, bool] = None,
**movable_properties):
super(Controllable, self).__init__(**movable_properties)
# self._move_state = { LEFT: False, RIGHT: False, UP: False, DOWN: False }
self._move_state = move_state
def set_move_state(self, direction):
self._move_state[direction] = not self._move_state[direction]
def update_position_from_user_input(self):
# some logic
self.update_position()
def update_angle_from_user_input(self, direction, magnitude):
# some logic
self.update_angle()
现在,如果您需要更改输入逻辑,您只需要更改Controllable
类方法中的特定位,但不需要对更新位置做任何操作,这仍然将由基类的方法处理。