2010-12-11 35 views
23

Gần đây tôi đã học được rằng các câu lệnh chuyển đổi là không tốt trong OOP, giống với từ "Mã sạch" (p37-39) của Robert Martin.Báo cáo chuyển đổi có hại không?

Nhưng hãy xem cảnh này: Tôi đang viết máy chủ trò chơi, nhận tin nhắn từ khách hàng, chứa số nguyên cho biết hành động của người chơi, chẳng hạn như di chuyển, tấn công, chọn mục ... v.v. các hành động khác nhau. Khi tôi viết mã để xử lý các tin nhắn này, không có giải pháp nào tôi nghĩ đến, nó sẽ phải sử dụng chuyển đổi ở đâu đó. Tôi nên sử dụng mẫu nào nếu không chuyển câu lệnh?

+0

Xem [Báo cáo chuyển đổi lớn: OOP xấu? ] (http://stackoverflow.com/questions/505454/large-switch-statements-bad-oop) –

+0

Luôn tìm thấy điều này thú vị vì mã thay đổi rõ ràng là thêm một trường hợp khác trong câu lệnh switch nhưng thêm một lớp khác thì không. .. đôi khi người thuần túy OO có thể nhận được một chút "tôn giáo" ... –

+3

'' Được coi là có hại "Được coi là có hại '. – delnan

Trả lời

21

Nút chuyển cũng giống như bất kỳ cấu trúc điều khiển nào khác. Có những nơi mà nó là giải pháp tốt nhất/sạch nhất và nhiều nơi khác mà nó hoàn toàn không phù hợp. Nó chỉ là cách lạm dụng nhiều hơn các cấu trúc điều khiển khác. Trong thiết kế OO, thường được coi là thích hợp hơn trong một tình huống như của bạn để sử dụng các loại/lớp thông báo khác nhau kế thừa từ một lớp thông báo chung, sau đó sử dụng các phương thức quá tải để phân biệt "tự động" giữa các loại khác nhau. Trong một trường hợp như của bạn, bạn có thể sử dụng một liệt kê ánh xạ tới mã hành động của bạn, sau đó đính kèm một thuộc tính cho mỗi giá trị được liệt kê sẽ cho phép bạn sử dụng generics hoặc type-building để xây dựng các đối tượng lớp con Action khác nhau sao cho phương thức nạp chồng sẽ hoạt động.

Nhưng đó là một nỗi đau thực sự.

Đánh giá xem có tùy chọn thiết kế nào chẳng hạn như điều tra khả thi trong giải pháp của bạn. Nếu không, chỉ cần sử dụng công tắc.

+1

Tôi đoán bạn có nghĩa là đa hình năng động chứ không phải là đa hình tĩnh (* phương pháp quá tải *) – Geek

+0

@Toby Bạn có thể vui lòng xây dựng câu lệnh này với ví dụ hay không: "sau đó đính kèm một thuộc tính cho mỗi giá trị được liệt kê sẽ cho phép bạn sử dụng generics hoặc type-building các đối tượng lớp con Hành động khác nhau sao cho phương thức nạp chồng sẽ hoạt động. "? – beinghuman

13

Mẫu Strategy có trong đầu bạn.

Mẫu chiến lược nhằm cung cấp phương tiện xác định nhóm thuật toán, đóng gói từng đối tượng làm đối tượng và làm cho chúng có thể hoán đổi cho nhau. Mô hình chiến lược cho phép các thuật toán thay đổi độc lập với khách hàng sử dụng chúng.

Trong trường hợp này, "họ thuật toán" là các hành động khác nhau của bạn.


Đối với báo cáo chuyển đổi - trong "Mã sạch", Robert Martin nói rằng ông cố gắng để hạn chế bản thân để một switch tuyên bố mỗi loại. Không loại bỏ chúng hoàn toàn.

Lý do là câu lệnh chuyển đổi không tuân thủ OCP.

3

Tôi sẽ đặt các thư trong một mảng và sau đó đối sánh mục đó với khóa giải pháp để hiển thị thông báo.

+0

không phải mọi thứ đều phải là OOP. Thực sự thích câu trả lời này. – grasshopper

14

'Báo cáo chuyển đổi' xấu thường là những chuyển đổi trên loại đối tượng (hoặc một cái gì đó có thể là một loại đối tượng trong thiết kế khác). Nói cách khác, hardcoding một cái gì đó mà có thể được xử lý tốt hơn bởi đa hình. Các loại báo cáo chuyển đổi khác cũng có thể là OK

Bạn sẽ cần một câu lệnh chuyển đổi, nhưng chỉ một câu lệnh chuyển đổi. Khi bạn nhận được thông báo, hãy gọi một đối tượng Factory để trả về một đối tượng của lớp con Message thích hợp (Move, Attack, vv), sau đó gọi một phương thức message-> doit() để thực hiện công việc.

Điều đó có nghĩa là nếu bạn thêm nhiều loại tin nhắn, chỉ có đối tượng nhà máy phải thay đổi.

+0

Điều gì về việc thực hiện những thứ như 'Bản đồ , Thing>' điều này rất giống với "thực hiện chuyển đổi trên lớp học", tuy nhiên điều này có được thực hiện không? – YoTengoUnLCD

4

Từ phối cảnh mẫu thiết kế, bạn có thể sử dụng Mẫu lệnh cho trường hợp đã cho. (Xem http://en.wikipedia.org/wiki/Command_pattern).

Nếu bạn thấy mình liên tục sử dụng câu lệnh chuyển đổi trong mô hình OOP, đây là dấu hiệu cho thấy lớp học của bạn có thể không được thiết kế tốt. Giả sử bạn có một thiết kế thích hợp của các lớp siêu và phụ và một số tiền hợp lý của Đa hình. Logic đằng sau các câu lệnh switch phải được xử lý bởi các lớp con.

Để biết thêm thông tin về cách bạn loại bỏ các câu lệnh chuyển đổi này và giới thiệu các lớp con phù hợp, tôi khuyên bạn nên đọc chương đầu tiên về Tái cấu trúc bởi Martin Fowler. Hoặc bạn có thể tìm các trang trình bày tương tự tại đây http://www1.informatik.uni-wuerzburg.de/database/courses/pi2_ss03_dir/RefactoringExampleSlides.pdf. (Slide 44)

2

Báo cáo IMO switch không phải là xấu, nhưng cần tránh nếu có thể. Một giải pháp là sử dụng Map trong đó các phím là các lệnh và các giá trị Command các đối tượng có phương thức execute(). Hoặc List nếu lệnh của bạn là số và không có khoảng trống.

Tuy nhiên, thông thường, bạn sẽ sử dụng các câu lệnh switch khi triển khai các mẫu thiết kế; một ví dụ là sử dụng mẫu Chain of responsibility để xử lý các lệnh được cung cấp cho bất kỳ lệnh "id" hoặc "value" nào. (Mẫu Strategy cũng được đề cập đến.) Tuy nhiên, trong trường hợp của bạn, bạn cũng có thể xem xét mẫu Command.

Về cơ bản, trong OOP, bạn sẽ cố gắng sử dụng các giải pháp khác ngoài việc dựa vào các khối switch, sử dụng mô hình lập trình theo thủ tục. Tuy nhiên, khi nào và làm thế nào để sử dụng hoặc là phần nào quyết định của bạn. Cá nhân tôi thường sử dụng switch khối khi sử dụng mô hình Factory, vv


Một định nghĩa của tổ chức mã này là:

  • một gói là một nhóm các lớp học với API coherant (ví dụ: Collection API trong nhiều khuôn khổ)
  • một lớp là một tập hợp các chức năng mạch lạc (ví dụ: Math lớp ...
  • phương thứcmột chức năng; nó nên làm một điều và một điều duy nhất. (ví dụ: thêm một mục trong danh sách có thể yêu cầu phóng to danh sách đã nêu, trong trường hợp này, phương pháp add sẽ dựa vào các phương pháp khác để thực hiện điều đó và sẽ không thực hiện thao tác đó, vì đó không phải là hợp đồng.)

Do đó, nếu tuyên bố switch của bạn thực hiện các loại hoạt động khác nhau, bạn đang "vi phạm" định nghĩa đó; trong khi sử dụng một mẫu thiết kế thì không phải vì mỗi hoạt động được định nghĩa trong lớp riêng của nó (nó là tập các chức năng riêng).

1

Sử dụng lệnh. Quấn các hành động trong một đối tượng và để cho đa hình làm việc chuyển đổi cho bạn. Trong C++ (shared_ptr chỉ đơn giản là một con trỏ, hoặc một tham chiếu trong các thuật ngữ Java.Nó cho phép cử động):

void GameServer::perform_action(shared_ptr<Action> op) { 
    op->execute(); 
} 

Khách hàng chọn một hành động để thực hiện, và một khi họ làm họ gửi rằng hành động riêng của mình đến máy chủ để máy chủ không cần phải làm bất cứ phân tích cú pháp:

void BlueClient::play() { 
    shared_ptr<Action> a; 
    if(should_move()) a = new Move(this, NORTHWEST); 
    else if(should_attack()) a = new Attack(this, EAST); 
    else a = Wait(this); 
    server.perform_action(a); 
} 
1

Tôi không mua nó. Những zealot OOP dường như có những máy có RAM vô hạn và hiệu năng tuyệt vời. Rõ ràng với RAM không giới hạn, bạn không phải lo lắng về việc phân mảnh RAM và các tác động hiệu suất có khi bạn liên tục tạo và phá hủy các lớp trợ giúp nhỏ. Để diễn giải câu trích dẫn cho sách 'Quy tắc đẹp' - "Mọi vấn đề trong Khoa học máy tính đều có thể được giải quyết với cấp độ trừu tượng khác"

Sử dụng nút chuyển nếu bạn cần. Trình biên dịch khá tốt khi tạo mã cho chúng.

+2

Bạn đang định cấu trúc lại rồi tối ưu hóa. Làm theo cách khác xung quanh không có ý nghĩa gì cả. – Eva

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