对集合的CRUD操作应用单一职责原则
我正在尝试理解如何合理地分离CRUD(创建、读取、更新、删除)职责,以遵循单一职责原则(SRP)。
根据我对SRP的理解,单一职责不一定是指一个单独的行为,而是可以是一组行为,这些行为之间有明确的逻辑边界。
在我的例子中,RestaurantMenu只是一个集合。我知道还有更有效的方式来表示这个,比如用字典,但这不是这个例子的重点。我的RestaurantMenu没有分配任何行为,因为我不确定再定义其他行为是否会违反SRP。通过一个管理对象来实例化和调用不同的CRUD对象,而不是通过RestaurantMenu中的方法,这让我感觉不太舒服,所以我决定在这里请教大家一些建议。
下面的例子符合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()
2 个回答
我想补充一下@guillaume31的回答,但我觉得在评论里说不太合适。
根据我对单一职责原则(SRP)的理解,单一职责不一定是指一个单一的行为,而是可以是一组行为,这些行为之间有明确的、逻辑上的边界。
不过,尽管你说你理解这个原则,但从你的代码来看,似乎并不是这样。你把一组紧密相关的任务分散到了几个类里。这样做有什么问题呢?
你有多少次写过下面这样的代码呢?
def __init__(self, menu):
self._menu = menu
我懒得去数,但你会发现这是一种不必要的代码重复。
在这个简单的例子中,确实没有什么问题,但如果你的应用程序变得越来越复杂,你就会头疼不已。
在一些国家,明天就是情人节,所以你应该记得要遵循KISS原则。
SRP(单一职责原则)合规的一个可能定义是:一个类应该只有一个变动的理由。
这让我们在抽象层面上判断一段代码是否符合SRP变得很困难,因为这基本上取决于你应用程序中哪些部分会一起发展,哪些部分会单独发展。
一般来说,用户界面(UI)是程序中一个主要可能独立发展的部分。用户在项目进行过程中总是希望对显示进行一些小调整,能够在不影响系统其他部分的情况下修改展示逻辑是件好事。数据持久化也是一个你可能想要改变的部分,这可能是因为新的架构决策,或者根据具体情况临时调整(比如在测试中用虚拟的持久化对象)。
所以在大多数实际应用中,我倾向于根据技术责任来拆分类,而不是根据同一个实体的业务操作,比如创建、读取、更新和删除(C/R/U/D)。
如果你仔细看看你当前的实现,你会发现类之间有一些模式。它们都在操作一个MenuManager
,以及存储在其中的MenuItems
。它们都在屏幕上打印东西。
如果你想改变数据的显示或存储方式,你基本上需要修改所有这些类。我并不是说在像这样的简单小系统中这是个严重的问题,但在一个更大的应用中,这可能会成为一个问题。
换句话说,你的例子使得通过图形界面对SQL数据库进行菜单更新、通过命令行对平面文件进行菜单插入,以及通过网络服务收集数据生成XML文件进行菜单读取变得很简单。这可能在某些特定情况下是你想要做的,但大多数时候并不是这样……