Các cách để loại bỏ chuyển đổi trong mã [đóng]


175

Các cách để loại bỏ việc sử dụng chuyển đổi trong mã là gì?


18
Tại sao bạn muốn loại bỏ các công tắc, khi được sử dụng một cách thích hợp? Xin vui lòng bạn có thể giải thích về câu hỏi của bạn.
RB.

Tôi bỏ phiếu câu hỏi này để cộng đồng có thể chỉnh sửa vì mọi người đều nói giống nhau và có thể tốt hơn để củng cố tất cả các ý kiến;)
Josh

2
Switch không chuẩn như các hướng dẫn khác. Chẳng hạn, trong C ++, bạn có khả năng quên 'phá vỡ' và sau đó bạn sẽ có kết quả không mong muốn. Bên cạnh đó, 'break' này quá giống với GOTO. Tôi chưa bao giờ cố gắng loại bỏ các công tắc, nhưng chắc chắn chúng không phải là hướng dẫn yêu thích của tôi. ;)

2
Tôi nghĩ rằng anh ấy đang đề cập đến khái niệm chuyển đổi chứ không phải câu lệnh chuyển đổi C ++ hoặc Java. Một công tắc cũng có thể là một chuỗi các khối 'nếu-khác nếu'.
Lập trình viên ngoài vòng pháp luật

2
Bạn có thể thích thú: có một số lượng lớn lập trình viên đáng chú ý từ cộng đồng nhanh nhẹn tham gia chiến dịch chống IF: antiifcampaign.com/supporters.html
Mike A

Câu trả lời:


267

Các câu lệnh chuyển đổi không phải là một antipotype per se, nhưng nếu bạn đang mã hóa đối tượng được định hướng, bạn nên xem xét liệu việc sử dụng một chuyển đổi có được giải quyết tốt hơn với đa hình thay vì sử dụng một câu lệnh chuyển đổi.

Với đa hình, điều này:

foreach (var animal in zoo) {
    switch (typeof(animal)) {
        case "dog":
            echo animal.bark();
            break;

        case "cat":
            echo animal.meow();
            break;
    }
}

trở thành thế này:

foreach (var animal in zoo) {
    echo animal.speak();
}

2
Tôi bị bash vì đề xuất tương tự trong stackoverflow.com/questions/374239/NH Vì vậy, nhiều người không tin vào đa hình :) Ví dụ rất hay.
Nazgob

30
-1: Tôi chưa bao giờ sử dụng câu lệnh chuyển đổi typeofvà câu trả lời này không đề xuất các cách hoặc lý do để khắc phục các câu lệnh chuyển đổi trong các tình huống khác.
Kevin

3
Tôi đồng ý với @Kevin - ví dụ được đưa ra không thực sự chỉ ra cách loại bỏ chuyển đổi bằng đa hình. Các ví dụ đơn giản: lấy tên của enum bằng cách chuyển đổi các giá trị của nó hoặc thực hiện một số mã theo các giá trị phù hợp trong một số loại thuật toán.
HotJard

Tôi đã tự hỏi làm thế nào để loại bỏ nó trước khi bạn có thể dựa vào các triển khai cụ thể, tức là trong một nhà máy. Và tôi đã tìm thấy một ví dụ tuyệt vời ở đây stackoverflow.com/a
43234505/711855

1
ví dụ rất tầm thường.
GuardianX

239

Xem Mùi báo cáo chuyển đổi :

Thông thường, các câu lệnh chuyển đổi tương tự nằm rải rác trong một chương trình. Nếu bạn thêm hoặc xóa một mệnh đề trong một chuyển đổi, bạn cũng thường phải tìm và sửa chữa các mệnh đề khác.

Cả Tái cấu trúcTái cấu trúc cho các mẫu đều có cách tiếp cận để giải quyết vấn đề này.

Nếu mã (giả) của bạn trông giống như:

class RequestHandler {

    public void handleRequest(int action) {
        switch(action) {
            case LOGIN:
                doLogin();
                break;
            case LOGOUT:
                doLogout();
                break;
            case QUERY:
               doQuery();
               break;
        }
    }
}

Mã này vi phạm Nguyên tắc Đóng mở và dễ vỡ đối với mọi loại mã hành động mới đi kèm. Để khắc phục điều này, bạn có thể giới thiệu một đối tượng 'Lệnh':

interface Command {
    public void execute();
}

class LoginCommand implements Command {
    public void execute() {
        // do what doLogin() used to do
    }
}

class RequestHandler {
    private Map<Integer, Command> commandMap; // injected in, or obtained from a factory
    public void handleRequest(int action) {
        Command command = commandMap.get(action);
        command.execute();
    }
}

Nếu mã (giả) của bạn trông giống như:

class House {
    private int state;

    public void enter() {
        switch (state) {
            case INSIDE:
                throw new Exception("Cannot enter. Already inside");
            case OUTSIDE:
                 state = INSIDE;
                 ...
                 break;
         }
    }
    public void exit() {
        switch (state) {
            case INSIDE:
                state = OUTSIDE;
                ...
                break;
            case OUTSIDE:
                throw new Exception("Cannot leave. Already outside");
        }
    }

Sau đó, bạn có thể giới thiệu một đối tượng 'Bang'.

// Throw exceptions unless the behavior is overriden by subclasses
abstract class HouseState {
    public HouseState enter() {
        throw new Exception("Cannot enter");
    }
    public HouseState leave() {
        throw new Exception("Cannot leave");
    }
}

class Inside extends HouseState {
    public HouseState leave() {
        return new Outside();
    }
}

class Outside extends HouseState {
    public HouseState enter() {
        return new Inside();
    }
}

class House {
    private HouseState state;
    public void enter() {
        this.state = this.state.enter();
    }
    public void leave() {
        this.state = this.state.leave();
    }
}

Hi vọng điêu nay co ich.


7
Cảm ơn ví dụ tuyệt vời về cách cấu trúc lại mã. Mặc dù tôi có thể nói rằng trong lúc
năn nỉ,

8
Các đối số chống lại chuyển đổi là hợp lệ miễn là bạn nhận ra rằng giải pháp đa hình hy sinh tính đơn giản của mã. Ngoài ra, nếu bạn luôn lưu trữ các trường hợp chuyển đổi của mình trong enums, một số trình biên dịch sẽ cảnh báo bạn rằng các trạng thái bị thiếu trong chuyển đổi.
Harvey

1
Đây là một ví dụ tuyệt vời về các hoạt động hoàn chỉnh / không đầy đủ và tất nhiên tái cấu trúc mã thành OOP. Cảm ơn rất nhiều. Tôi nghĩ nó sẽ hữu ích nếu những người đề xuất OOP / Design Forms đề nghị đối xử với các khái niệm OOP như các toán tử hơn là các khái niệm . Tôi có nghĩa là "mở rộng", "nhà máy", "thực hiện", vv được sử dụng rất thường xuyên trên các tệp, lớp, nhánh. Chúng nên đơn giản như các toán tử như "+", "-", "+ =", "?:", "==", "->", v.v. sau đó anh ta có thể suy nghĩ trên toàn bộ thư viện lớp về trạng thái chương trình và (trong) các hoạt động hoàn chỉnh.
không gian tên

13
Tôi bắt đầu nghĩ rằng SWITCH dễ hiểu và hợp lý hơn nhiều so với điều này. Và tôi thường thích OOP rất nhiều, nhưng độ phân giải này có vẻ quá trừu tượng đối với tôi
JK

1
Với đối tượng Command, mã để tạo Map<Integer, Command>không cần chuyển đổi?
ataulm

41

Công tắc là một mẫu, cho dù được thực hiện bằng câu lệnh chuyển đổi, nếu chuỗi khác, bảng tra cứu, đa hình oop, khớp mẫu hoặc một cái gì đó khác.

Bạn có muốn loại bỏ việc sử dụng " tuyên bố chuyển đổi " hoặc " mẫu chuyển đổi " không? Cái thứ nhất có thể được loại bỏ, cái thứ hai, chỉ khi có thể sử dụng một mẫu / thuật toán khác và hầu hết thời gian là không thể hoặc đó không phải là cách tiếp cận tốt hơn để làm như vậy.

Nếu bạn muốn loại bỏ câu lệnh chuyển đổi khỏi mã, câu hỏi đầu tiên cần đặt ra là việc loại bỏ câu lệnh chuyển đổi và sử dụng một số kỹ thuật khác có ý nghĩa gì . Thật không may, câu trả lời cho câu hỏi này là tên miền cụ thể.

Và hãy nhớ rằng trình biên dịch có thể thực hiện các tối ưu hóa khác nhau để chuyển đổi các câu lệnh. Vì vậy, ví dụ nếu bạn muốn xử lý tin nhắn một cách hiệu quả, một câu lệnh chuyển đổi là cách tốt nhất để đi. Nhưng mặt khác, chạy các quy tắc kinh doanh dựa trên câu lệnh chuyển đổi có lẽ không phải là cách tốt nhất và ứng dụng nên được nghiên cứu lại.

Dưới đây là một số lựa chọn thay thế để chuyển đổi tuyên bố:


1
Ai đó có thể đưa ra một so sánh xử lý tin nhắn bằng cách sử dụng chuyển đổi so với các lựa chọn thay thế khác?
Mike A

37

Chuyển đổi bản thân nó không tệ, nhưng nếu bạn có nhiều "công tắc" hoặc "nếu / khác" trên các đối tượng trong phương thức của mình thì đó có thể là một dấu hiệu cho thấy thiết kế của bạn hơi "thủ tục" và các đối tượng của bạn chỉ là giá trị xô. Di chuyển logic đến các đối tượng của bạn, gọi một phương thức trên các đối tượng của bạn và để chúng quyết định cách trả lời thay thế.


Tất nhiên, giả sử rằng anh ta không viết bằng C. :)
Bernard

1
trong C, anh ta có thể (ab?) sử dụng các con trỏ hàm và cấu trúc để xây dựng một cái gì đó giống như đối tượng;)
Tetha

Bạn có thể viết FORT ^ H ^ H ^ H ^ H Java bằng bất kỳ ngôn ngữ nào. ; p
Bernard

Hoàn toàn đồng ý - chuyển đổi là cách tuyệt vời để giảm các dòng mã, nhưng đừng chơi với nó quá mach.
HotJard

21

Tôi nghĩ cách tốt nhất là sử dụng Bản đồ tốt. Sử dụng từ điển, bạn có thể ánh xạ hầu hết mọi đầu vào sang một số giá trị / đối tượng / hàm khác.

mã của bạn sẽ trông giống như thế nào (psuedo) như thế này:

void InitMap(){
    Map[key1] = Object/Action;
    Map[key2] = Object/Action;
}

Object/Action DoStuff(Object key){
    return Map[key];
}

4
Phụ thuộc vào ngôn ngữ. Nó có thể nhận được ít dễ đọc hơn một công tắc
Vinko Vrsalovic

Đây là một giải pháp thanh lịch tốt đẹp trong tình huống phù hợp. Tôi đã thực hiện mã khóa ánh xạ này gần đây và dường như nó đọc ổn cho mục đích đó.
Bernard

Điều này là đúng, tôi có thể sẽ không sử dụng điều này cho bất cứ điều gì đơn giản, nhưng nó cung cấp một số tính linh hoạt về mặt cấu hình so với câu lệnh chuyển đổi. Một từ điển có thể được chuẩn bị một cách nhanh chóng trong khi một công tắc sẽ luôn được mã hóa cứng.
Josh

Có thể sạch hơn trong một số trường hợp. Cũng chậm hơn, vì nó yêu cầu chức năng gọi.
Nick Johnson

1
Điều đó phụ thuộc vào chìa khóa của bạn. Trình biên dịch có thể biên dịch một câu lệnh chuyển đổi thành một tra cứu đơn giản hoặc tìm kiếm nhị phân tĩnh cực nhanh, trong cả hai trường hợp mà không yêu cầu bất kỳ lệnh gọi hàm nào.
Nick Johnson

12

Mọi người đều yêu thích if elsecác khối HUGE . Rất dễ đọc! Tôi tò mò về lý do tại sao bạn muốn loại bỏ các câu lệnh chuyển đổi, mặc dù. Nếu bạn cần một câu lệnh chuyển đổi, có lẽ bạn cần một câu lệnh chuyển đổi. Nghiêm túc mà nói, tôi muốn nói nó phụ thuộc vào những gì mã đang làm. Nếu tất cả các chuyển đổi đang làm là gọi các chức năng (giả sử), bạn có thể vượt qua các con trỏ hàm. Cho dù đó là một giải pháp tốt hơn là tranh cãi.

Ngôn ngữ là một yếu tố quan trọng ở đây cũng, tôi nghĩ.


13
Tôi cho rằng đó là mỉa mai :)
Ngày Craig

6

Tôi nghĩ rằng những gì bạn đang tìm kiếm là Mô hình chiến lược.

Điều này có thể được thực hiện theo một số cách, đã được đề cập trong các câu trả lời khác cho câu hỏi này, chẳng hạn như:

  • Bản đồ các giá trị -> hàm
  • Đa hình. (loại phụ của một đối tượng sẽ quyết định cách nó xử lý một quy trình cụ thể).
  • Chức năng hạng nhất.

5

switch câu lệnh sẽ tốt để thay thế nếu bạn thấy mình thêm trạng thái mới hoặc hành vi mới vào câu lệnh:

trạng thái int;

Chuỗi getString () {
   chuyển đổi (trạng thái) {
     trường hợp 0: // hành vi cho trạng thái 0
           trả về "không";
     trường hợp 1: // hành vi cho trạng thái 1
           trả lại "một";
   }
   ném IllegalStateException mới ();
}

nhân đôi getDouble () {

   chuyển đổi (this.state) {
     trường hợp 0: // hành vi cho trạng thái 0
           trả về 0đ;
     trường hợp 1: // hành vi cho trạng thái 1
           trả lại 1đ;
   }
   ném IllegalStateException mới ();
}

Thêm hành vi mới yêu cầu sao chép switchvà thêm trạng thái mới có nghĩa là thêm trạng thái khác casevào mỗi switch câu lệnh.

Trong Java, bạn chỉ có thể chuyển đổi một số lượng rất hạn chế các kiểu nguyên thủy có giá trị bạn biết khi chạy. Điều này thể hiện một vấn đề trong chính nó: các trạng thái đang được thể hiện dưới dạng số hoặc ký tự ma thuật.

Kết hợp mẫu và nhiều if - elsekhối có thể được sử dụng, mặc dù thực sự có cùng các vấn đề khi thêm các hành vi mới và trạng thái mới.

Giải pháp mà những người khác đã đề xuất là "đa hình" là một ví dụ của mẫu Trạng thái :

Thay thế mỗi trạng thái bằng lớp riêng của nó. Mỗi hành vi có phương thức riêng trên lớp:

Nhà nước IState;

Chuỗi getString () {
   trả về trạng thái.getString ();
}

nhân đôi getDouble () {
   trả về trạng thái.getDouble ();
}

Mỗi khi bạn thêm một trạng thái mới, bạn phải thêm một triển khai IStategiao diện mới. Trong một switchthế giới, bạn sẽ thêm một casecho mỗi switch.

Mỗi lần bạn thêm một hành vi mới, bạn cần thêm một phương thức mới vào IStategiao diện và mỗi cách thực hiện. Đây là gánh nặng tương tự như trước đây, mặc dù bây giờ trình biên dịch sẽ kiểm tra xem bạn có triển khai hành vi mới trên mỗi trạng thái tồn tại trước đó không.

Những người khác đã nói rằng, điều này có thể quá nặng nề, vì vậy tất nhiên có một điểm bạn đạt đến nơi bạn di chuyển từ nơi này sang nơi khác. Cá nhân tôi, lần thứ hai tôi viết một công tắc là điểm mà tôi tái cấu trúc.



3

Chà, trước tiên, tôi không biết sử dụng switch là một kiểu chống.

Thứ hai, chuyển đổi luôn có thể được thay thế bằng câu lệnh if / other if.


chính xác - chuyển đổi chỉ là methadone cú pháp cho một loạt if / elsifs.
Mike A

3

Tại sao bạn muốn Trong tay một trình biên dịch tốt, một câu lệnh chuyển đổi có thể hiệu quả hơn nhiều so với các khối if / khác (cũng như dễ đọc hơn) và chỉ các công tắc lớn nhất mới có khả năng được tăng tốc nếu chúng được thay thế bởi bất kỳ loại nào của cấu trúc dữ liệu tra cứu gián tiếp.


2
Tại thời điểm đó, bạn đoán được trình biên dịch thứ hai và thực hiện các thay đổi đối với thiết kế của bạn dựa trên các phần bên trong trình biên dịch. Thiết kế nên tuân theo bản chất của vấn đề, không phải bản chất của trình biên dịch.
Mike A

3

'Switch' chỉ là một cấu trúc ngôn ngữ và tất cả các cấu trúc ngôn ngữ có thể được coi là công cụ để hoàn thành công việc. Như với các công cụ thực tế, một số công cụ phù hợp với một nhiệm vụ hơn một công việc khác (bạn sẽ không sử dụng búa tạ để đưa ra một cái móc tranh). Phần quan trọng là cách "hoàn thành công việc" được xác định. Nó cần phải được bảo trì, nó có cần phải nhanh, có cần mở rộng quy mô không, có cần phải mở rộng hay không.

Tại mỗi điểm trong quy trình lập trình thường có một loạt các cấu trúc và các mẫu có thể được sử dụng: một công tắc, một chuỗi if-if-if, hàm ảo, bảng nhảy, bản đồ với các con trỏ hàm, v.v. Với kinh nghiệm lập trình viên sẽ biết theo bản năng công cụ phù hợp để sử dụng cho một tình huống nhất định.

Phải giả định rằng bất kỳ ai duy trì hoặc xem xét mã ít nhất đều có kỹ năng như tác giả ban đầu để mọi cấu trúc có thể được sử dụng một cách an toàn.


OK, nhưng tại sao chúng ta cần 5 cách khác nhau, dư thừa để làm cùng một việc - thực hiện có điều kiện?
Mike A

@ mike.amy: Bởi vì mỗi phương pháp có lợi ích và chi phí khác nhau và tất cả là về việc nhận được nhiều lợi ích nhất với chi phí thấp nhất.
Skizz

1

Nếu công tắc ở đó để phân biệt giữa các loại đối tượng khác nhau, có lẽ bạn đang thiếu một số lớp để mô tả chính xác các đối tượng đó hoặc một số phương thức ảo ...


1

Dành cho C ++

Nếu bạn đang đề cập đến một ví dụ về AbstractFactory tôi nghĩ rằng phương thức registerCreatorFunc (..) thường tốt hơn là yêu cầu thêm một trường hợp cho mỗi câu lệnh "mới" cần thiết. Sau đó, cho phép tất cả các lớp tạo và đăng ký một creatorFunction (..) có thể dễ dàng thực hiện với một macro (nếu tôi dám đề cập). Tôi tin rằng đây là một cách tiếp cận phổ biến nhiều khung làm. Lần đầu tiên tôi nhìn thấy nó trong ET ++ và tôi nghĩ rằng nhiều khung công tác yêu cầu macro DECL và IMPL sử dụng nó.


1

Trong một ngôn ngữ thủ tục, như C, sau đó chuyển đổi sẽ tốt hơn bất kỳ thay thế nào.

Trong một ngôn ngữ hướng đối tượng, sau đó hầu như luôn có các lựa chọn thay thế khác có sẵn để sử dụng tốt hơn cấu trúc đối tượng, đặc biệt là đa hình.

Vấn đề với các câu lệnh chuyển đổi phát sinh khi một số khối chuyển đổi rất giống nhau xảy ra ở nhiều nơi trong ứng dụng và cần phải thêm hỗ trợ cho một giá trị mới. Một điều khá phổ biến đối với một nhà phát triển là quên thêm hỗ trợ cho giá trị mới vào một trong các khối chuyển đổi nằm rải rác xung quanh ứng dụng.

Với tính đa hình, sau đó một lớp mới thay thế giá trị mới và hành vi mới được thêm vào như là một phần của việc thêm lớp mới. Hành vi tại các điểm chuyển đổi này sau đó được kế thừa từ siêu lớp, được ghi đè để cung cấp hành vi mới hoặc được thực hiện để tránh lỗi trình biên dịch khi siêu phương thức là trừu tượng.

Khi không có sự đa hình rõ ràng đang diễn ra, nó có thể có giá trị thực hiện mô hình Chiến lược .

Nhưng nếu sự thay thế của bạn là một NẾU lớn ... THÌ ... khối ELSE, thì hãy quên nó đi.


1

Sử dụng ngôn ngữ không đi kèm với câu lệnh chuyển đổi tích hợp. Perl 5 đến với tâm trí.

Nghiêm túc mà nói, tại sao bạn lại muốn tránh nó? Và nếu bạn có lý do chính đáng để tránh nó, tại sao không đơn giản là tránh nó sau đó?


1

Các con trỏ hàm là một cách để thay thế một câu lệnh chuyển đổi khổng lồ, chúng đặc biệt tốt trong các ngôn ngữ nơi bạn có thể nắm bắt các hàm theo tên của chúng và tạo ra các thứ với chúng.

Tất nhiên, bạn không nên buộc các câu lệnh chuyển đổi ra khỏi mã của mình và luôn có khả năng bạn đang làm sai tất cả, điều này dẫn đến các đoạn mã dư thừa ngu ngốc. (Điều này đôi khi không thể tránh khỏi, nhưng một ngôn ngữ tốt sẽ cho phép bạn loại bỏ sự dư thừa trong khi vẫn sạch sẽ.)

Đây là một ví dụ tuyệt vời và chinh phục:

Nói rằng bạn có một thông dịch viên của một số loại.

switch(*IP) {
    case OPCODE_ADD:
        ...
        break;
    case OPCODE_NOT_ZERO:
        ...
        break;
    case OPCODE_JUMP:
        ...
        break;
    default:
        fixme(*IP);
}

Thay vào đó, bạn có thể sử dụng điều này:

opcode_table[*IP](*IP, vm);

... // in somewhere else:
void opcode_add(byte_opcode op, Vm* vm) { ... };
void opcode_not_zero(byte_opcode op, Vm* vm) { ... };
void opcode_jump(byte_opcode op, Vm* vm) { ... };
void opcode_default(byte_opcode op, Vm* vm) { /* fixme */ };

OpcodeFuncPtr opcode_table[256] = {
    ...
    opcode_add,
    opcode_not_zero,
    opcode_jump,
    opcode_default,
    opcode_default,
    ... // etc.
};

Lưu ý rằng tôi không biết cách loại bỏ sự dư thừa của opcode_table trong C. Có lẽ tôi nên đặt câu hỏi về nó. :)


0

Câu trả lời rõ ràng nhất, độc lập với ngôn ngữ là sử dụng một loạt 'nếu'.

Nếu ngôn ngữ bạn đang sử dụng có con trỏ hàm (C) hoặc có các hàm là giá trị hạng 1 (Lua), bạn có thể đạt được kết quả tương tự như "chuyển đổi" bằng cách sử dụng một mảng (hoặc danh sách) các hàm (con trỏ tới).

Bạn nên cụ thể hơn về ngôn ngữ nếu bạn muốn câu trả lời tốt hơn.


0

Báo cáo chuyển đổi thường có thể được thay thế bằng một thiết kế OO tốt.

Ví dụ: bạn có một lớp Tài khoản và đang sử dụng câu lệnh chuyển đổi để thực hiện một phép tính khác dựa trên loại tài khoản.

Tôi sẽ đề nghị rằng điều này nên được thay thế bằng một số lớp tài khoản, đại diện cho các loại tài khoản khác nhau và tất cả đều thực hiện giao diện Tài khoản.

Việc chuyển đổi sau đó trở nên không cần thiết, vì bạn có thể xử lý tất cả các loại tài khoản giống nhau và nhờ tính đa hình, phép tính phù hợp sẽ được chạy cho loại tài khoản.


0

Phụ thuộc vào lý do tại sao bạn muốn thay thế nó!

Nhiều thông dịch viên sử dụng 'gotos tính toán' thay vì các câu lệnh chuyển đổi để thực thi opcode.

Điều tôi nhớ về chuyển đổi C / C ++ là Pascal 'in' và các phạm vi. Tôi cũng ước tôi có thể bật chuỗi. Nhưng những thứ này, trong khi tầm thường để một trình biên dịch ăn, là công việc khó khăn khi được thực hiện bằng cách sử dụng các cấu trúc và các trình vòng lặp và mọi thứ. Vì vậy, ngược lại, có rất nhiều điều tôi ước mình có thể thay thế bằng một công tắc, nếu chỉ có công tắc của C () linh hoạt hơn!


0

Chuyển đổi không phải là một cách tốt để đi vì nó phá vỡ Hiệu trưởng Mở Đóng. Đây là cách tôi làm điều đó.

public class Animal
{
       public abstract void Speak();
}


public class Dog : Animal
{
   public virtual void Speak()
   {
       Console.WriteLine("Hao Hao");
   }
}

public class Cat : Animal
{
   public virtual void Speak()
   {
       Console.WriteLine("Meauuuu");
   }
}

Và đây là cách sử dụng nó (lấy mã của bạn):

foreach (var animal in zoo) 
{
    echo animal.speak();
}

Về cơ bản những gì chúng tôi đang làm là giao trách nhiệm cho lớp trẻ thay vì cha mẹ quyết định phải làm gì với trẻ.

Bạn cũng có thể muốn đọc lên "Nguyên tắc thay thế Liskov".


0

Trong JavaScript sử dụng mảng Associative:
this:

function getItemPricing(customer, item) {
    switch (customer.type) {
        // VIPs are awesome. Give them 50% off.
        case 'VIP':
            return item.price * item.quantity * 0.50;

            // Preferred customers are no VIPs, but they still get 25% off.
        case 'Preferred':
            return item.price * item.quantity * 0.75;

            // No discount for other customers.
        case 'Regular':
        case
        default:
            return item.price * item.quantity;
    }
}

trở thành thế này:

function getItemPricing(customer, item) {
var pricing = {
    'VIP': function(item) {
        return item.price * item.quantity * 0.50;
    },
    'Preferred': function(item) {
        if (item.price <= 100.0)
            return item.price * item.quantity * 0.75;

        // Else
        return item.price * item.quantity;
    },
    'Regular': function(item) {
        return item.price * item.quantity;
    }
};

    if (pricing[customer.type])
        return pricing[customer.type](item);
    else
        return pricing.Regular(item);
}

Phép lịch sự


-12

Một phiếu khác cho nếu / khác. Tôi không phải là một fan hâm mộ lớn của các trường hợp hoặc tuyên bố chuyển đổi bởi vì có một số người không sử dụng chúng. Mã ít đọc hơn nếu bạn sử dụng case hoặc switch. Có thể không ít người đọc đối với bạn, nhưng với những người chưa bao giờ cần sử dụng lệnh.

Các nhà máy đối tượng cũng vậy.

Nếu / khối khác là một cấu trúc đơn giản mà mọi người đều có. Có một vài điều bạn có thể làm để đảm bảo rằng bạn không gây ra sự cố.

Thứ nhất - Đừng thử và thụt lề nếu câu lệnh nhiều hơn một vài lần. Nếu bạn thấy mình thụt lề, thì bạn đã làm sai.

 if a = 1 then 
     do something else 
     if a = 2 then 
         do something else
     else 
         if a = 3 then 
             do the last thing
         endif
     endif 
  endif

Thực sự là xấu - làm điều này thay vào đó.

if a = 1 then 
   do something
endif 
if a = 2 then 
   do something else
endif 
if a = 3 then 
   do something more
endif 

Tối ưu hóa bị nguyền rủa. Nó không tạo ra nhiều sự khác biệt so với tốc độ mã của bạn.

Thứ hai, tôi không phản đối việc thoát ra khỏi Khối nếu miễn là có đủ các câu lệnh ngắt nằm rải rác trong khối mã cụ thể để làm cho nó rõ ràng

procedure processA(a:int)
    if a = 1 then 
       do something
       procedure_return
    endif 
    if a = 2 then 
       do something else
       procedure_return
    endif 
    if a = 3 then 
       do something more
       procedure_return
    endif 
end_procedure

EDIT : On Switch và tại sao tôi nghĩ thật khó để mò mẫm:

Đây là một ví dụ về câu lệnh switch ...

private void doLog(LogLevel logLevel, String msg) {
   String prefix;
   switch (logLevel) {
     case INFO:
       prefix = "INFO";
       break;
     case WARN:
       prefix = "WARN";
       break;
     case ERROR:
       prefix = "ERROR";
       break;
     default:
       throw new RuntimeException("Oops, forgot to add stuff on new enum constant");
   }
   System.out.println(String.format("%s: %s", prefix, msg));
 }

Đối với tôi, vấn đề ở đây là các cấu trúc điều khiển thông thường áp dụng trong các ngôn ngữ C giống như đã bị phá vỡ hoàn toàn. Có một quy tắc chung là nếu bạn muốn đặt nhiều hơn một dòng mã trong cấu trúc điều khiển, bạn sử dụng dấu ngoặc nhọn hoặc câu lệnh bắt đầu / kết thúc.

ví dụ

for i from 1 to 1000 {statement1; statement2}
if something=false then {statement1; statement2}
while isOKtoLoop {statement1; statement2}

Đối với tôi (và bạn có thể sửa tôi nếu tôi sai), câu lệnh Case ném quy tắc này ra khỏi cửa sổ. Một khối mã được thực hiện có điều kiện không được đặt bên trong cấu trúc bắt đầu / kết thúc. Vì điều này, tôi tin rằng Case đủ khác biệt về mặt khái niệm để không được sử dụng.

Hy vọng rằng câu trả lời câu hỏi của bạn.


Wow - rõ ràng là một câu trả lời gây tranh cãi. Tôi muốn biết những gì tôi đã sai.
seanyboy

Er, chuyển đổi như là quá phức tạp? Tôi không biết ... dường như đối với tôi sẽ không còn nhiều tính năng ngôn ngữ mà bạn có thể sử dụng. :) Ngoài ra, trong ví dụ trung tâm của bạn, sẽ không thông minh hơn nếu (a == 1 || a == 2 || a == 3) làm gì đó?
Lars Westergren

Ngoài ra, trong ví dụ cuối cùng của bạn, "break" sẽ không làm bất cứ điều gì trong hầu hết các ngôn ngữ - thoát ra khỏi khối gần nhất (thường là một vòng lặp), trong trường hợp của bạn dù sao cũng xảy ra ở dòng tiếp theo (endif). Nếu bạn sử dụng ngôn ngữ mà giờ nghỉ là "trở lại", nhiều lợi nhuận cũng sẽ được tán thành (ngoại trừ "tuyên bố bảo vệ")
Lars Westergren

4
"Tối ưu hóa bị nguyền rủa. Nó không tạo ra nhiều sự khác biệt so với tốc độ mã của bạn." Mã của tôi chạy trên một nền tảng di động. Tối ưu hóa vấn đề. Hơn nữa, các công tắc có thể làm cho mã trông RẤT sạch (so với if..elseif..elseif..elseif ...) nếu được sử dụng đúng cách. Chưa bao giờ nhìn thấy chúng? Học chúng.
Swati

Switch hoàn toàn không phức tạp, nhưng tôi luôn cố gắng giảm thiểu số lượng cấu trúc được sử dụng trong mã để giảm ma sát.
seanyboy
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.