针对集合的CRUD操作的单一责任原则

时间:2014-06-11 10:41:37

标签: python oop design-patterns

我试图理解如何在逻辑上区分CRUD职责,以便遵守单一责任原则(SRP)。

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

在我的例子中,RestaurantMenu只不过是一个集合。我知道有更有效的方法来表示这一点,例如使用字典,但这超出了本示例的意图。我的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 个答案:

答案 0 :(得分:2)

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

这使得在抽象代码中调用SRP变得非常困难 - 它基本上取决于在应用程序中随着时间的推移一起发生的变化。

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

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

如果仔细查看当前的实施情况,您会注意到课程中的模式。他们都使用MenuManagerMenuItems 存储。他们所有打印的东西都在屏幕上。

如果您想要以数据的显示或存储方式进行更改,您基本上必须触摸所有这些类。我不是说这是一个像这样的小型简单系统的严重缺陷,但在更大的应用程序中它可能是一个问题。

换句话说,您的示例可以轻松地通过图形界面将菜单更新完成到SQL数据库中,通过命令shell将菜单插入到平面文件中,并且菜单读取使用从a收集的数据吐出XML文件网络服务。这可能是你想要在非常特殊的情况下做的事情,但不是大多数时候......

答案 1 :(得分:2)

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

  

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

但是你说你明白这一点,你的代码就是相反的。您通过几个课程传播了一组高度凝聚力的任务。为什么这很糟糕?

您有多少次以下代码?

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

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

在这个特殊的简单案例中,没有任何问题,但是如果你的应用程序增长,你将会非常头疼。

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