对集合的CRUD操作应用单一职责原则

2 投票
2 回答
1260 浏览
提问于 2025-04-18 09:24

我正在尝试理解如何合理地分离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 个回答

2

我想补充一下@guillaume31的回答,但我觉得在评论里说不太合适。

根据我对单一职责原则(SRP)的理解,单一职责不一定是指一个单一的行为,而是可以是一组行为,这些行为之间有明确的、逻辑上的边界。

不过,尽管你说你理解这个原则,但从你的代码来看,似乎并不是这样。你把一组紧密相关的任务分散到了几个类里。这样做有什么问题呢?

你有多少次写过下面这样的代码呢?

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

我懒得去数,但你会发现这是一种不必要的代码重复。

在这个简单的例子中,确实没有什么问题,但如果你的应用程序变得越来越复杂,你就会头疼不已。

在一些国家,明天就是情人节,所以你应该记得要遵循KISS原则

3

SRP(单一职责原则)合规的一个可能定义是:一个类应该只有一个变动的理由。

这让我们在抽象层面上判断一段代码是否符合SRP变得很困难,因为这基本上取决于你应用程序中哪些部分会一起发展,哪些部分会单独发展。

一般来说,用户界面(UI)是程序中一个主要可能独立发展的部分。用户在项目进行过程中总是希望对显示进行一些小调整,能够在不影响系统其他部分的情况下修改展示逻辑是件好事。数据持久化也是一个你可能想要改变的部分,这可能是因为新的架构决策,或者根据具体情况临时调整(比如在测试中用虚拟的持久化对象)。

所以在大多数实际应用中,我倾向于根据技术责任来拆分类,而不是根据同一个实体的业务操作,比如创建、读取、更新和删除(C/R/U/D)。

如果你仔细看看你当前的实现,你会发现类之间有一些模式。它们都在操作一个MenuManager,以及存储在其中的MenuItems。它们都在屏幕上打印东西。

如果你想改变数据的显示或存储方式,你基本上需要修改所有这些类。我并不是说在像这样的简单小系统中这是个严重的问题,但在一个更大的应用中,这可能会成为一个问题。

换句话说,你的例子使得通过图形界面对SQL数据库进行菜单更新、通过命令行对平面文件进行菜单插入,以及通过网络服务收集数据生成XML文件进行菜单读取变得很简单。这可能在某些特定情况下是你想要做的,但大多数时候并不是这样……

撰写回答