2012-11-01 39 views
7

Tôi có nền C và là một newb trên C++. Tôi có một câu hỏi thiết kế cơ bản. Tôi có một lớp (Tôi sẽ gọi nó là "đầu bếp" b/c vấn đề tôi có vẻ rất tương tự như này, cả về độ phức tạp và các vấn đề) mà về cơ bản các công trình như thế nàyC++ Trợ giúp về tái cấu trúc lớp quái vật

class chef 
    { 
    public: 
      void prep(); 
      void cook(); 
      void plate(); 

    private: 
      char name; 
      char dish_responsible_for; 
      int shift_working; 
      etc... 
    } 

trong mã giả, điều này được triển khai dọc theo các dòng:

int main{ 
    chef my_chef; 
    kitchen_class kitchen; 
    for (day=0; day < 365; day++) 
     { 
     kitchen.opens(); 
     .... 

     my_chef.prep(); 
     my_chef.cook(); 
     my_chef.plate(); 

     .... 

     kitchen.closes(); 
     } 
    } 

Lớp đầu bếp ở đây có vẻ là lớp quái vật và có tiềm năng trở thành một lớp. đầu bếp cũng dường như vi phạm các nguyên tắc trách nhiệm duy nhất, vì vậy thay vì chúng ta nên có cái gì đó như:

class employee 
    { 
    protected: 
     char name; 
     int shift_working; 
    } 

    class kitchen_worker : employee 
    { 
    protected: 
     dish_responsible_for; 
    } 

    class cook_food : kitchen_worker 
    { 
    public: 
     void cook(); 
     etc... 
    } 
    class prep_food : kitchen_worker 
    { 
    public: 
     void prep(); 
     etc... 
    } 

 class plater : kitchen_worker 
    { 
    public: 
     void plate(); 
    } 

vv ...

Tôi phải thừa nhận là vẫn phải vật lộn với cách thực hiện nó trong thời gian chạy để, nếu ví dụ plater (hoặc "đầu bếp trong khả năng của mình như plater") quyết định về nhà giữa dịch vụ ăn tối, sau đó các đầu bếp đã làm việc một sự thay đổi mới.

Điều này dường như liên quan đến một câu hỏi rộng hơn, nếu cùng một người làm việc chuẩn bị, nấu ăn và mạ trong ví dụ này, lợi thế thực tế thực tế của việc phân cấp lớp này là gì làm? Tôi đoán điều đó xảy ra với điều "sợ thêm lớp học", nhưng đồng thời, ngay bây giờ hoặc trong tương lai gần, tôi không nghĩ việc duy trì lớp học đầu bếp trong toàn bộ nó là khủng khiếp rườm rà. Tôi cũng nghĩ rằng đó là một cảm giác thật sự dễ dàng hơn cho người đọc ngây thơ của mã để xem ba phương pháp khác nhau trong đối tượng đầu bếp và tiếp tục.

Tôi hiểu rằng nó có thể đe dọa trở nên khó sử dụng khi chúng ta thêm các phương thức như "cut_onions()", "cut_carrots()", v.v ..., có lẽ mỗi dữ liệu của riêng họ bằng cách làm cho hàm prep(), nói, mô đun hơn. Hơn nữa, có vẻ như SRP đưa đến kết luận hợp lý của nó sẽ tạo ra một lớp "onion_cutters" "carrot_cutters" vv ... và tôi vẫn gặp khó khăn khi nhìn thấy giá trị của điều đó, bởi vì bằng cách nào đó chương trình phải đảm bảo rằng cùng một nhân viên cắt hành và cà rốt giúp giữ biến trạng thái giống nhau trên các phương pháp (ví dụ, nếu nhân viên cắt hành ngón tay của mình thì anh ta không còn đủ điều kiện để cắt cà rốt nữa), trong khi ở lớp đầu bếp đối tượng quái vật thì có vẻ như tất cả đều được chăm sóc. Tất nhiên, tôi hiểu rằng điều này sau đó trở nên ít hơn về việc có một "thiết kế hướng đối tượng" có ý nghĩa, nhưng dường như tôi phải có các đối tượng riêng biệt cho mỗi nhiệm vụ của đầu bếp (có vẻ không tự nhiên, cho rằng cùng một người đang làm cả ba chức năng) thì dường như ưu tiên thiết kế phần mềm trên mô hình khái niệm. Tôi cảm thấy một thiết kế hướng đối tượng là hữu ích ở đây nếu chúng ta muốn có, nói, "thịt_chef" "sous_chef" "three_star_chef" có khả năng những người khác nhau. Hơn nữa, liên quan đến vấn đề thời gian chạy là có một chi phí phức tạp, theo ứng dụng nghiêm ngặt của nguyên tắc trách nhiệm duy nhất, phải đảm bảo dữ liệu cơ bản tạo nên nhân viên của lớp cơ sở thay đổi và thay đổi này là được phản ánh trong các bước thời gian tiếp theo.

Do đó, tôi rất muốn từ bỏ nó nhiều hơn hoặc ít hơn. Nếu ai đó có thể làm rõ lý do tại sao điều này sẽ là một ý tưởng tồi (và nếu bạn có đề xuất về cách tốt nhất để tiến hành) tôi sẽ có nghĩa vụ nhất.

+0

Đôi khi lập bản đồ thực vai trò thế giới/trách nhiệm/nhiệm vụ cho các đối tượng trong mã chỉ không hoạt động. Có lẽ bạn cần một số chức năng chung mà có một người và một hành động. Chức năng đó sẽ giúp người đó áp dụng hành động. –

+0

mô-đun trên mỗi giao diện lớp học có thể cung cấp cho bạn nhiều manh mối hơn? giống như những gì một plater có thể làm gì? cook_food có thể làm gì? họ có cần phải kế thừa hay chỉ là một kỹ năng (gọi hàm)? – billz

+0

Nhìn vào phương pháp bố cục. Hoặc có thể bạn cần một mô hình nhà nước ở đây? –

Trả lời

3

Để tránh lạm dụng quyền thừa kế lớp học ngay bây giờ và trong tương lai, bạn thực sự chỉ nên sử dụng nó khi có mối quan hệ .Như chính bạn, "là cook_food một kitchen_worker". Nó rõ ràng là không có ý nghĩa trong cuộc sống thực, và cũng không có trong mã. "cook_food" là một hành động, do đó, có thể có ý nghĩa khi tạo lớp hành động và lớp con thay thế.

Có một lớp mới chỉ để thêm các phương thức mới như cook()prep() thực sự không phải là cải thiện về vấn đề ban đầu - vì tất cả những gì bạn đã làm được bao bọc trong phương thức. Những gì bạn thực sự muốn là làm cho một trừu tượng để làm bất kỳ những hành động này - vì vậy trở lại lớp hành động.

class action { 
    public: 
     virtual void perform_action()=0; 
} 

class cook_food : public action { 
    public: 
     virtual void perform_action() { 
      //do cooking; 
     } 
} 

Sau đó, đầu bếp có thể được cung cấp danh sách các tác vụ để thực hiện theo thứ tự bạn chỉ định. Ví dụ, một hàng đợi.

class chef { 
    ... 
     perform_actions(queue<action>& actions) { 
      for (action &a : actions) { 
       a.perform_action(); 
      } 
     } 
    ... 
} 

Điều này thường được gọi là Strategy Pattern. Nó khuyến khích nguyên tắc mở/đóng, bằng cách cho phép bạn thêm các hành động mới mà không sửa đổi các lớp hiện có của bạn.


Một cách tiếp cận thay thế, bạn có thể sử dụng là một Template Method, nơi bạn chỉ định một chuỗi các bước trừu tượng, và sử dụng các lớp con để thực hiện các hành vi cụ thể cho từng người.

class dish_maker { 
    protected: 
     virtual void prep() = 0; 
     virtual void cook() = 0; 
     virtual void plate() = 0; 

    public: 
     void make_dish() { 
      prep(); 
      cook(); 
      plate(); 
     } 
} 

class onion_soup_dish_maker : public dish_maker { 
    protected: 
     virtual void prep() { ... } 
     virtual void cook() { ... } 
     virtual void plate() { ... } 
} 

Một mô hình liên quan chặt chẽ mà có thể phù hợp cho điều này là Builder Pattern

Những mẫu cũng có thể làm giảm các anti-pattern Sequential Coupling, vì nó là tất cả quá dễ dàng để quên gọi một số phương pháp, hoặc gọi số chúng theo đúng thứ tự, đặc biệt nếu bạn làm nó nhiều lần. Bạn cũng có thể xem xét việc đặt kitchen.opens() và đóng() vào một phương thức mẫu tương tự, hơn là bạn không cần phải lo lắng về việc đóng() được gọi.


On tạo các lớp học riêng cho onion_cutter và carrot_cutter, điều này là không thực sự kết luận logic của SRP, nhưng trong thực tế, một sự vi phạm của nó - bởi vì bạn đang làm cho các lớp học mà chịu trách nhiệm cắt, và giữ một số thông tin về những gì họ đang cắt. Cả hành tây cắt và củ cà rốt đều có thể được trừu tượng thành một hành động cắt - và bạn có thể chỉ định đối tượng nào cần cắt và thêm chuyển hướng cho từng lớp riêng lẻ nếu bạn cần mã cụ thể cho từng đối tượng.

Một bước sẽ là tạo ra một trừu tượng để nói điều gì đó là cuttable. Các mối quan hệ cho phân lớp là ứng viên, vì cà rốt có thể cắt.

class cuttable { 
    public: 
     virtual void cut()=0; 
} 

class carrot : public cuttable { 
    public: 
     virtual void cut() { 
      //specific code for cutting a carrot; 
     } 
} 

Hành động cắt có thể mất một đối tượng cuttable và thực hiện bất kỳ hành động cắt phổ biến mà là áp dụng đối với tất cả cuttables, và cũng có thể áp dụng các hành vi cắt cụ thể của từng đối tượng.

class cutting_action : public action { 
    private: 
     cuttable* object; 
    public: 
     cutting_action(cuttable* obj) : object(obj) { } 
     virtual void perform_action() { 
      //common cutting code 
      object->cut(); //specific cutting code 
     } 
} 

+0

Cảm ơn vì điều đó. Tôi muốn theo dõi nó bằng một câu hỏi. Vì vậy, nếu chúng ta thực hiện theo phương pháp tiếp cận mẫu chiến lược, hãy cho biết mỗi đầu bếp có nhiều thành viên dữ liệu (ví dụ: tasting_spoon, salt_shaker, v.v ...) được sử dụng trong cả lớp cook_food() và plate_dish(). Có vẻ như đầu bếp lớp cần cook_food() và cook_food() cần đầu bếp lớp. Làm thế nào để tránh sự phụ thuộc vòng tròn giữa cook_food() và đầu bếp lớp mà không cần phải đưa ra một tấn đối số cho lớp cook_food()? Từ quan điểm thiết kế, nếu các lớp được kết hợp cao, không có sự phân biệt nào để tách chúng? – user1790399

+0

Một cách để tránh sự phụ thuộc vòng tròn là tạo ra một giao diện đầu bếp, mà lớp cook_food có thể tiêu thụ và lớp đầu bếp thực tế có thể thực hiện. Miễn là giao diện chỉ xác định các chi tiết cần thiết cho cook_food để biết về đầu bếp, thì không có vấn đề gì. (hoặc bạn có thể làm theo cách khác, và tạo ra một giao diện nấu ăn cho đầu bếp để tiêu thụ.) Không có cách nào đúng, nhưng chắc chắn có công đức để tách từng công việc thành lớp riêng của nó. –

+0

Xin lỗi, một lớp học có thể tiêu thụ một giao diện có ý nghĩa gì? Nó có nghĩa là làm một cái gì đó như, nếu chúng ta đi với giao diện trên lớp đầu bếp, ví dụ, cook_food (chef-> interface_for_cooking()), với interface_for_cooking() tạo thành các cuộc gọi như get_salt_shaker(), get_tasting_spoon() v.v. .? Và để làm theo với ví dụ này, sẽ là một ý tưởng tồi khi có hai giao diện riêng biệt cho các lớp cook_food() và plate_dish()? – user1790399

Các vấn đề liên quan