2014-06-11 42 views
2

我试图理解如何从逻辑上区分CRUD职责,以便遵守单一责任原则(SRP)。针对集合的CRUD操作的单一职责原则

正如我理解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()                   

回答

2

SRP合规的一个可能的定义是应该只有one reason for a class to change

这使得在摘要中的一段代码中很难调用SRP - 它基本上取决于在应用程序中随着时间的推移一起发展和发生什么。

一般来说,UI是可能独立于程序其他部分发展的主要事物之一。用户会一直希望在项目过程中进行少量的显示调整,并且能够修改表示逻辑而不用担心破坏系统的其他部分,这是一件好事。持久性是您可能想要更改的另一件事,无论是作为新架构决策的结果,还是暂时的,取决于上下文(例如在测试中交换虚拟持久对象)。

这就是为什么在大多数现实世界的应用程序中,我倾向于将技术职责分成类,而不是像C/R/U/D这样的实体上的业务操作。

如果仔细观察当前的实现,您会注意到类中的模式。他们全都拨弄着MenuManagerMenuItems存储在其中。他们都打印事情到屏幕上。

如果你想改变数据显示或存储的方式,你基本上必须触及所有这些类。我并不是说这是一个像这样的小型简单系统的严重缺陷,但是在更大的应用程序中,这可能是一个问题。换句话说,您的示例可以很容易地通过图形界面将菜单更新插入到SQL数据库中,菜单通过命令shell插入到平面文件中,菜单读取将XML数据从收集的数据中分离出来一个Web服务。这可能是你想在特殊情况下做的事情,但不是大部分时间...

+0

我开始明白了。你所描述的设计会是什么样子?你介意分享一个小提琴吗? – Dowwie

+1

恐怕我的Python技能有点受限,但这里有一个很方便的例子:https://github.com/Finglas/Playground/blob/master/hexagonal.architecture/example.py你会认识到流行的设计模式,比如作为帮助您实施SRP的存储库和控制器。所提到的整体方法是六角形建筑(Hexalonal Architecture)(http://alistair.cockburn.us/Hagagonal+architecture) - 在这样的小程序中它并不重要,但它的想法是识别单个大型同心圆区域,责任对象在你的系统中,每个区域本身具有较高的单一责任。 – guillaume31

+0

[本演示文稿]的第一部分(http://fr.slideshare.net/DrTrucho/python-solid)也说明了我在代码中所说的内容。 – guillaume31

2

我只是想补充@ guillaume31的答案,但我不认为它会适合评论。

正如我理解SRP的定义,单个责任可能不一定是单一行为,而是一个具有明确定义的逻辑边界的行为集合。

然而,你说你明白这一点,你的代码显示相反。你已经通过几个班级传播了一个高度凝聚力的任务组。为什么这是不好的?

你有几次下面的代码?

def __init__(self, menu):             
     self._menu = menu 

我懒得指望它,但你会注意到这是一个不必要的代码重复。

在这个特别简单的例子中,事实上没有问题,但是如果你的应用程序增长了,你将会头痛不已。

在某些国家,明天是情人节,所以你应该记住如何KISS

+0

谢谢你澄清这一点。获得的教训 - 将CRUD放在一起以增加凝聚力。 – Dowwie