我试图理解如何在逻辑上分离CRUD责任,从而遵守单一责任原则(SRP)。
正如我对SRP的定义所理解的那样,一个单一的责任不一定是一个单一的行为,而是一个行为的集合,与其他行为有着明确的逻辑界限。
在我的例子中,RestaurantMenu不过是一个集合。我知道有更有效的方法来表示这一点,例如使用字典,但这超出了本例的意图。我的餐厅菜单没有指定给它的行为,因为我仍然不清楚用它来定义任何进一步的行为是否会违反SRP。通过Manager对象(而不是RestaurantMenu中的方法)实例化和调用单独的CRUD对象感觉很不舒服,所以我决定向这里的听众寻求一些指导。
以下示例是否通过SRP石蕊测试?
class RestaurantMenu(object):
def __init__(self, title, creator, catalog_type, restaurant):
self._title = title
self._creator = creator
self._catalog_type = catalog_type
self._restaurant = restaurant
self._menuitems = dict()
class MenuManager(object):
"""Responsibility
--------------
Coordinates CRUD related activities with a menu
"""
def __init__(self, menu):
self._menu = menu
def add_menu_item(self, item, value):
menu_item_adder = AddMenuItem(self._menu)
menu_item_adder(item, value)
def del_menu_item(self, item):
menu_item_deleter = DelMenuItem(self._menu)
menu_item_deleter(item)
def update_menu_item(self, existing_item, new_info):
menu_item_updater = UpdateMenuItem(self._menu)
menu_item_updater(existing_item, new_info)
def get_menu_items(self):
menu_item_getter = GetMenuItems(self._menu)
menu_item_getter()
class GetMenuItems(object):
def __init__(self, menu):
self._menu = menu
def __call__(self):
print(self._menu._title)
print('='*len(self._menu._title))
for key, value in self._menu._menuitems.items():
print(key, value)
class AddMenuItem(object):
def __init__(self, menu):
self._menu = menu
def __call__(self, item, value):
if item not in self._menu._menuitems:
self._menu._menuitems[item] = value
print('Item added:', item)
else:
print('Item already exists. Please update instead.')
class DelMenuItem(object):
def __init__(self, menu):
self._menu = menu
def __call__(self, item):
popped = self._menu._menuitems.pop(item)
print('Item removed:', popped)
class UpdateMenuItem(object):
def __init__(self, menu):
self._menu = menu
def __call__(self, existing_item, new_info):
self._menu._menuitems.update(existing_item=new_info)
print('Item updated:', existing_item, ' with', new_info)
def main():
mymenu = RestaurantMenu("Joe's Crab Shack 2014 Menu",
"Joe Schmoe",
"Restaurant",
"Joe's Crab Shack")
menumanager = MenuManager(mymenu)
menumanager.add_menu_item('longneck_clams', 7.00)
menumanager.add_menu_item('1 pound lobster', 15.00)
menumanager.add_menu_item('lobster chowder', 9.00)
print('-'*50)
menumanager.get_menu_items()
if __name__ == "__main__":
main()
最佳答案
SRP合规性的一个可能定义是只应存在one reason for a class to change。
这使得在抽象的代码中调用SRP或不调用SRP变得非常困难——这基本上取决于随着时间的推移,在应用程序中会发生什么样的变化。
不过,一般来说,UI是可能独立于程序其他部分发展的主要内容之一。用户会一直希望在项目过程中进行一些显示调整,能够修改表示逻辑而不必担心破坏系统的其余部分,这是一件好事。持久性是另一件您可能希望更改的事情,可能是由于新的体系结构决策,也可能是临时更改,具体取决于上下文(例如,在测试中交换虚拟持久性对象)。
这就是为什么在大多数实际应用程序中,我倾向于按技术责任而不是像C/R/U/D这样的同一实体上的业务操作来划分类。
如果仔细观察当前的实现,您会注意到类中的模式他们都在摆弄一个MenuManager
和其中存储的MenuItems
。他们都把东西印在屏幕上。
如果你想改变数据显示或存储的方式,你基本上必须接触所有这些类。我并不是说在这样一个小而简单的系统中这是一个严重的缺陷,但在一个更大的应用程序中,这很可能是一个问题。
换句话说,您的示例使通过图形界面将菜单更新到SQL数据库中变得容易,通过命令shell将菜单插入到平面文件中变得容易,并且菜单读取从web服务收集数据的XML文件。这可能是你在非常特殊的情况下想做的,但不是大多数时候。。。