วิธีกำจัดสวิตช์ในรหัส [ปิด]


175

มีวิธีใดบ้างในการกำจัดการใช้สวิตช์ในโค้ด


18
ทำไมคุณต้องการกำจัดสวิตช์เมื่อใช้อย่างเหมาะสม? โปรดอธิบายรายละเอียดให้กับคำถามของคุณ
RB

ผมลงคะแนนคำถามที่จะทำนี้สามารถแก้ไขได้ชุมชนเพราะทุกคนไม่ว่าจะเป็นสิ่งเดียวกันและมันอาจจะดีที่จะรวบรวมความคิดเห็นทั้งหมด;)
จอช

2
สวิตช์ไม่ได้มาตรฐานเหมือนคำแนะนำอื่น ๆ ตัวอย่างเช่นใน C ++ คุณมีโอกาสที่จะลืม 'break' จากนั้นคุณจะได้ผลลัพธ์ที่ไม่คาดคิด นอกจากนี้ 'การหยุด' นี้ก็คล้ายกับ GOTO มากเกินไป ฉันไม่เคยพยายามกำจัดสวิตช์ แต่แน่นอนว่าไม่ใช่คำสั่งโปรดของฉัน ;)

2
ฉันคิดว่าเขาอ้างถึงแนวคิดสวิตช์ไม่ใช่คำสั่งสวิตช์ C ++ หรือ Java สวิตช์สามารถเป็นโซ่ของบล็อก 'if-else if'
Outlaw Programmer

2
คุณอาจจะสนุก: มีจำนวนมากของการเขียนโปรแกรมที่โดดเด่นจากชุมชนเปรียว joinng การรณรงค์ต่อต้าน-IF: antiifcampaign.com/supporters.html
ไมค์

คำตอบ:


267

คำสั่ง Switch ไม่ใช่ antipattern ต่อ se แต่ถ้าคุณเขียนโค้ดเชิงวัตถุคุณควรพิจารณาว่าการใช้สวิตช์นั้นแก้ไขได้ดีกว่าด้วยpolymorphismแทนที่จะใช้คำสั่ง switch

ด้วยความหลากหลายนี้:

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

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

กลายเป็นสิ่งนี้:

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

2
ฉันถูกทุบเพราะข้อเสนอแนะที่คล้ายกันในstackoverflow.com/questions/374239/ ...... ppl จำนวนมากไม่เชื่อใน polymorphism :) เป็นตัวอย่างที่ดีมาก
Nazgob

30
-1: ฉันไม่เคยใช้คำสั่ง switch กับtypeofและคำตอบนี้ไม่แนะนำวิธีหรือเหตุผลในการแก้ไขคำสั่ง switch ในสถานการณ์อื่น ๆ
Kevin

3
ฉันเห็นด้วยกับ @Kevin - ตัวอย่างที่ให้มาไม่ได้แสดงให้เห็นถึงวิธีกำจัดสวิตช์โดย polymorphism อย่างแท้จริง ตัวอย่างง่าย ๆ : รับชื่อ enum โดยการสลับค่าหรือดำเนินการโค้ดบางอย่างด้วยค่าที่เหมาะสมในอัลกอริทึมบางประเภท
HotJard

ฉันสงสัยว่าจะกำจัดได้อย่างไรก่อนที่คุณจะสามารถใช้งานได้เฉพาะเช่นในโรงงาน และฉันพบตัวอย่างที่ยอดเยี่ยมได้ที่นี่stackoverflow.com/a/3434505/711855
juanmf

1
ตัวอย่างเล็ก ๆ น้อย ๆ
GuardianX

239

ดูกลิ่นของงบ Switch :

โดยทั่วไปแล้วงบสวิตช์ที่คล้ายกันจะกระจัดกระจายไปทั่วทั้งโปรแกรม หากคุณเพิ่มหรือลบส่วนคำสั่งในสวิตช์หนึ่งคุณมักจะต้องค้นหาและซ่อมแซมส่วนอื่นด้วย

ทั้งการเปลี่ยนโครงสร้างและการเปลี่ยนโครงสร้างให้เป็นรูปแบบมีวิธีแก้ไขปัญหานี้

หากรหัส (หลอก) ของคุณดูเหมือนว่า:

class RequestHandler {

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

รหัสนี้ละเมิดหลักการ Open Openและมีความเปราะบางต่อรหัสการกระทำทุกรูปแบบที่มาพร้อมกัน เพื่อแก้ไขสิ่งนี้คุณสามารถแนะนำวัตถุ 'คำสั่ง':

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();
    }
}

หากรหัส (หลอก) ของคุณดูเหมือนว่า:

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");
        }
    }

จากนั้นคุณสามารถแนะนำวัตถุ 'สถานะ'

// 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();
    }
}

หวังว่านี่จะช่วยได้


7
ขอบคุณสำหรับตัวอย่างที่ดีเกี่ยวกับวิธีการสร้างรหัสใหม่ แต่ผมอาจจะบอกว่าในจุดเริ่มต้นมันเป็นเพียงเล็กน้อยยากที่จะอ่าน (เพราะหนึ่งมีการสลับไปมาระหว่างหลายไฟล์ที่จะสมบูรณ์เข้าใจมัน)
rshimoda

8
ข้อโต้แย้งต่อสวิตช์มีผลตราบเท่าที่คุณทราบว่าโซลูชัน polymorphic ลดความซับซ้อนของรหัสลง นอกจากนี้หากคุณเก็บกล่องสวิทช์ของคุณทุกครั้งใน enums คอมไพเลอร์บางตัวจะเตือนคุณว่าสถานะหายไปจากสวิตช์
ฮาร์วีย์

1
นี่เป็นตัวอย่างที่ดีเกี่ยวกับการดำเนินการที่สมบูรณ์ / ไม่สมบูรณ์และแน่นอนว่าการปรับโครงสร้างโค้ดเป็น OOP ขอบคุณมาก ฉันคิดว่ามันจะเป็นประโยชน์ถ้า OOP / ออกแบบรูปแบบการเสนอขอแนะนำให้รักษาแนวคิด OOP เช่นผู้ประกอบการมากกว่าแนวคิด ฉันหมายถึงว่า "ขยาย", "โรงงาน", "นำไปปฏิบัติ" ฯลฯ ถูกใช้บ่อยในไฟล์, คลาส, สาขา พวกเขาควรจะง่ายเหมือนโอเปอเรเตอร์เช่น "+", "-", "+ =", "?:", "==", "->" เป็นต้นเมื่อโปรแกรมเมอร์ใช้ในใจของเขาเหมือนกับผู้ปฏิบัติงานเท่านั้น จากนั้นเขาสามารถคิดข้ามไลบรารีคลาสเต็มเกี่ยวกับสถานะโปรแกรมและ (ใน) การดำเนินการที่สมบูรณ์
เนมสเปซ

13
ฉันเริ่มคิดว่าสวิทช์เป็นที่เข้าใจและมีเหตุผลมากกว่าเมื่อเปรียบเทียบกับสิ่งนี้ และฉันมักชอบ OOP มาก แต่ความละเอียดนี้ดูเหมือนเป็นนามธรรมสำหรับฉัน
JK

1
ด้วยวัตถุคำสั่งรหัสที่จะสร้างMap<Integer, Command>ไม่จำเป็นต้องมีสวิตช์หรือไม่
ataulm

41

สวิทซ์เป็นรูปแบบไม่ว่าจะนำมาใช้กับคำสั่งสวิทช์หากโซ่อื่นตารางการค้นหาความหลากหลายของรูปแบบการจับคู่รูปแบบหรืออย่างอื่น

คุณต้องการกำจัดการใช้ " คำสั่ง switch " หรือ " pattern pattern " หรือไม่? คนแรกสามารถถูกกำจัดออกได้คนที่สองก็ต่อเมื่อสามารถใช้รูปแบบ / อัลกอริธึมอื่นและใช้เวลาส่วนใหญ่ที่ไม่สามารถทำได้หรือไม่ใช่วิธีที่ดีกว่าในการทำเช่นนั้น

หากคุณต้องการกำจัดคำสั่ง switchออกจากโค้ดคำถามแรกที่ถามคือการใช้คำสั่ง switchและใช้เทคนิคอื่นที่ไหน น่าเสียดายที่คำตอบสำหรับคำถามนี้เป็นเฉพาะโดเมน

และจำไว้ว่าคอมไพเลอร์สามารถทำการปรับให้เหมาะสมต่าง ๆ เพื่อสลับคำสั่ง ตัวอย่างเช่นถ้าคุณต้องการทำการประมวลผลข้อความอย่างมีประสิทธิภาพงบ switch นั้นเป็นวิธีที่ดีมาก แต่ในทางกลับกันกฎเกณฑ์ทางธุรกิจที่ยึดตามคำสั่งสวิตช์อาจไม่ใช่วิธีที่ดีที่สุดที่จะไปและแอปพลิเคชันควรได้รับการออกแบบใหม่

นี่คือทางเลือกอื่นในการสลับคำสั่ง:


1
ใครช่วยเปรียบเทียบการประมวลผลข้อความโดยใช้สวิตช์กับทางเลือกอื่นได้หรือไม่?
ไมค์

37

การสลับในตัวมันเองนั้นไม่ได้เลวร้ายนัก แต่ถ้าคุณมี "สวิตช์" หรือ "ถ้า / อื่น ๆ " จำนวนมากบนวัตถุในวิธีการของคุณมันอาจเป็นสัญญาณว่าการออกแบบของคุณนั้นค่อนข้าง "ขั้นตอน" และวัตถุของคุณมีค่า บุ้งกี๋ ย้ายตรรกะไปยังวัตถุของคุณเรียกใช้วิธีการบนวัตถุของคุณและให้พวกเขาตัดสินใจว่าจะตอบสนองอย่างไร


สมมติว่าแน่นอนว่าเขาไม่ได้เขียนใน C. :)
เบอร์นาร์ด

1
ใน C เขาสามารถ (AB) คำแนะนำการใช้งานฟังก์ชั่นและ structs ที่จะสร้างบางสิ่งบางอย่างเช่นวัตถุ;)
Tetha

คุณสามารถเขียน FORT ^ H ^ H ^ H ^ H Java ในภาษาใดก็ได้ ; p
เบอร์นาร์ด

เห็นด้วยอย่างเต็มที่ - การเปลี่ยนเป็นวิธีที่ยอดเยี่ยมในการลดบรรทัดโค้ด แต่อย่าเล่นกับมันมากเกินไป
HotJard

21

ฉันคิดว่าวิธีที่ดีที่สุดคือใช้แผนที่ที่ดี การใช้พจนานุกรมคุณสามารถแมปอินพุตได้เกือบทุกอย่างกับค่า / วัตถุ / ฟังก์ชั่นอื่น ๆ

รหัสของคุณจะมีลักษณะบางอย่าง (psuedo) เช่นนี้:

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

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

4
ขึ้นอยู่กับภาษา มันสามารถอ่านได้น้อยกว่าสวิตช์
Vinko Vrsalovic

นี่เป็นทางออกที่ดีงามในสถานการณ์ที่เหมาะสม ฉันทำรหัสคีย์การแมปนี้เมื่อเร็ว ๆ นี้และดูเหมือนว่าจะอ่านไม่เป็นไรสำหรับจุดประสงค์นั้น
เบอร์นาร์ด

นี่เป็นเรื่องจริงฉันอาจจะไม่ใช้สิ่งนี้เพื่ออะไรที่เรียบง่าย แต่มันให้ความยืดหยุ่นในแง่ของการกำหนดค่าผ่านคำสั่งสวิตช์ พจนานุกรมสามารถเตรียมได้ทันทีในขณะที่สวิทช์จะถูกเข้ารหัสยากเสมอ
Josh

สามารถทำความสะอาดได้ในบางสถานการณ์ ช้าลงเนื่องจากต้องใช้การเรียกใช้ฟังก์ชัน
Nick Johnson

1
ขึ้นอยู่กับกุญแจของคุณ คอมไพเลอร์สามารถคอมไพล์คำสั่ง switch ให้เป็นการค้นหาแบบง่ายหรือการค้นหาไบนารีแบบสแตติกที่รวดเร็วมากไม่ว่าในกรณีใดโดยไม่จำเป็นต้องเรียกใช้ฟังก์ชันใด ๆ
Nick Johnson

12

ทุกคนชอบif elseบล็อกขนาดใหญ่ อ่านง่ายมาก! แต่ฉันอยากรู้ว่าทำไมคุณถึงต้องการลบคำสั่งสวิตช์ออก หากคุณต้องการคำสั่ง switch คุณอาจต้องใช้คำสั่ง switch อย่างจริงจังแม้ว่าฉันจะบอกว่ามันขึ้นอยู่กับสิ่งที่รหัสทำ หากสวิตช์ทั้งหมดกำลังทำหน้าที่เรียกฟังก์ชัน (พูด) คุณสามารถผ่านตัวชี้ฟังก์ชันได้ ไม่ว่าจะเป็นทางออกที่ดีกว่าเป็นที่ถกเถียงกัน

ฉันก็คิดว่าภาษาก็เป็นปัจจัยสำคัญเช่นกัน


13
ฉันสมมติว่ามันเป็นการเสียดสี :)
วัน Craig

6

ฉันคิดว่าสิ่งที่คุณกำลังมองหาคือรูปแบบกลยุทธ์

สามารถดำเนินการได้หลายวิธีซึ่งได้รับการกล่าวถึงในคำตอบอื่น ๆ สำหรับคำถามนี้เช่น:

  • แผนที่ค่า -> ฟังก์ชั่น
  • ความแตกต่าง (ประเภทย่อยของวัตถุจะตัดสินใจว่าจะจัดการกระบวนการเฉพาะ)
  • ฟังก์ชั่นชั้นหนึ่ง

5

switch คำชี้แจงจะเป็นการดีถ้าหากคุณพบว่าคุณกำลังเพิ่มสถานะใหม่หรือพฤติกรรมใหม่ให้กับคำสั่ง:

รัฐ int;

String getString () {
   สวิตช์ (สถานะ) {
     กรณีที่ 0: // พฤติกรรมสำหรับสถานะ 0
           ส่งคืน "ศูนย์";
     กรณีที่ 1: // พฤติกรรมสำหรับสถานะ 1
           ส่งคืน "หนึ่ง";
   }
   โยน IllegalStateException ใหม่ ();
}

getDouble คู่ () {

   สวิตช์ (this.state) {
     กรณีที่ 0: // พฤติกรรมสำหรับสถานะ 0
           กลับ 0d;
     กรณีที่ 1: // พฤติกรรมสำหรับสถานะ 1
           ผลตอบแทน 1d;
   }
   โยน IllegalStateException ใหม่ ();
}

การเพิ่มลักษณะการทำงานใหม่จำเป็นต้องมีการคัดลอกswitchและการเพิ่มสถานะใหม่หมายถึงการเพิ่มอีกครั้งcaseในทุก switchคำสั่ง

ใน Java คุณสามารถสลับประเภทดั้งเดิมได้ในจำนวน จำกัด ซึ่งมีค่าที่คุณรู้ขณะใช้งานจริง สิ่งนี้นำเสนอปัญหาในตัวของมันเอง: สถานะต่างๆจะถูกแสดงเป็นตัวเลขหรือตัวอักษรเวทย์มนตร์

การจับคู่รูปแบบและif - elseบล็อกจำนวนมากสามารถใช้ได้แม้ว่าจะมีปัญหาเหมือนกันเมื่อเพิ่มพฤติกรรมใหม่และสถานะใหม่

วิธีแก้ปัญหาที่คนอื่น ๆ แนะนำว่าเป็น "polymorphism" เป็นตัวอย่างของรูปแบบของรัฐ :

แทนที่แต่ละสถานะด้วยคลาสของตัวเอง แต่ละพฤติกรรมมีวิธีการของตัวเองในชั้นเรียน:

รัฐ istate;

String getString () {
   return state.getString ();
}

getDouble คู่ () {
   คืนค่า state.getDouble ();
}

ทุกครั้งที่คุณเพิ่มสถานะใหม่คุณต้องเพิ่มการใช้งานใหม่ของIStateอินเทอร์เฟซ ในswitchโลกนี้คุณจะเพิ่มcaseไปยังแต่ละswitchรายการ

ทุกครั้งที่คุณเพิ่มพฤติกรรมใหม่คุณต้องเพิ่มวิธีการใหม่ในIStateอินเทอร์เฟซและการใช้งานแต่ละรายการ นี่เป็นภาระเหมือนเดิมแม้ว่าตอนนี้คอมไพเลอร์จะตรวจสอบว่าคุณมีการใช้งานของพฤติกรรมใหม่ในแต่ละสถานะที่มีอยู่

คนอื่นพูดไปแล้วว่านี่อาจเป็นเฮฟวี่เวทมากเกินไปดังนั้นแน่นอนว่ามีจุดที่คุณไปถึงจุดที่คุณย้ายจากที่หนึ่งไปยังอีกที่หนึ่ง โดยส่วนตัวครั้งที่สองที่ฉันเขียนสวิตช์คือจุดที่ฉันสร้างใหม่



3

สำหรับหนึ่งฉันไม่รู้ว่าการใช้สวิตช์เป็นรูปแบบการต่อต้าน

ประการที่สองสวิตช์สามารถถูกแทนที่ด้วย if / else หากข้อความสั่ง


สลับ - เป็นเพียงเมทาโดน syntactic สำหรับพวงของถ้า / elsifs
ไมค์

3

ทำไมคุณถึงต้องการ ในมือของคอมไพเลอร์ที่ดีคำสั่งสวิตช์อาจมีประสิทธิภาพมากกว่าบล็อก / อื่น ๆ (เช่นเดียวกับที่อ่านง่ายกว่า) และสวิตช์ที่ใหญ่ที่สุดเท่านั้นที่จะถูกเร่งหากพวกเขาถูกแทนที่ด้วยประเภทใด ๆ ของโครงสร้างข้อมูลการค้นหาทางอ้อม


2
ณ จุดนี้คุณจะคาดเดาคอมไพเลอร์เป็นครั้งที่สองและทำการเปลี่ยนแปลงการออกแบบของคุณตามคอมไพเลอร์ภายใน การออกแบบควรเป็นไปตามลักษณะของปัญหาไม่ใช่ลักษณะของคอมไพเลอร์
ไมค์

3

'switch' เป็นเพียงโครงสร้างภาษาและโครงสร้างภาษาทั้งหมดสามารถใช้เป็นเครื่องมือในการทำงาน เช่นเดียวกับเครื่องมือจริงเครื่องมือบางอย่างเหมาะกับงานหนึ่งมากกว่าอีกงานหนึ่ง (คุณจะไม่ใช้ค้อนเลื่อนเพื่อวางตะขอรูปภาพ) ส่วนที่สำคัญคือการกำหนด 'การทำให้งานเสร็จสมบูรณ์' มันจำเป็นต้องได้รับการบำรุงรักษาหรือไม่จำเป็นต้องรวดเร็วหรือไม่จำเป็นต้องขยายขนาดหรือไม่และต้องยืดขยายได้เป็นต้น

ในแต่ละจุดในกระบวนการเขียนโปรแกรมมักจะมีช่วงของการสร้างและรูปแบบที่สามารถใช้ได้: สวิตช์ลำดับ if-else-if ฟังก์ชันเสมือนตารางกระโดดแผนที่ที่มีตัวชี้ฟังก์ชันและอื่น ๆ ด้วยประสบการณ์โปรแกรมเมอร์จะรู้เครื่องมือที่เหมาะสมที่จะใช้สำหรับสถานการณ์ที่กำหนด

จะต้องมีการสันนิษฐานว่าใครก็ตามที่ดูแลรักษาหรือตรวจสอบโค้ดอย่างน้อยมีทักษะเทียบเท่ากับผู้แต่งดั้งเดิมเพื่อให้การสร้างใด ๆ สามารถใช้งานได้อย่างปลอดภัย


ตกลง แต่ทำไมเราถึงต้องการ 5 วิธีที่ต่างกันและซ้ำซ้อนในการทำสิ่งเดียวกัน - การทำงานตามเงื่อนไข?
ไมค์

@ mike.amy: เพราะแต่ละวิธีมีประโยชน์และค่าใช้จ่ายที่แตกต่างกันและมันคือทั้งหมดที่เกี่ยวกับการได้รับประโยชน์สูงสุดด้วยต้นทุนที่น้อยที่สุด
Skizz

1

หากสวิตช์อยู่ตรงนั้นเพื่อแยกความแตกต่างระหว่างวัตถุประเภทต่างๆคุณอาจไม่มีคลาสบางตัวเพื่ออธิบายวัตถุเหล่านั้นอย่างแม่นยำหรือวิธีการเสมือนบางอย่าง ...


1

สำหรับ C ++

หากคุณอ้างถึงเช่น AbstractFactory ฉันคิดว่าโดยปกติแล้วเมธอดregisterCreatorFunc (.. )จะดีกว่าการเพิ่มเคสสำหรับแต่ละคำสั่ง "ใหม่" ที่จำเป็น จากนั้นให้ทุกคลาสสร้างและลงทะเบียนcreatorFunction (.. )ซึ่งสามารถนำไปใช้กับแมโครได้ง่าย (ถ้าฉันกล้าพูดถึง) ฉันเชื่อว่านี่เป็นวิธีการทั่วไปที่หลาย ๆ เฟรมเวิร์คทำ ฉันเห็นมันครั้งแรกใน ET ++ และฉันคิดว่าเฟรมเวิร์กจำนวนมากที่ต้องใช้มาโคร DECL และ IMPL ใช้มัน


1

ในภาษาเชิงโพรซีเดอร์เช่น C จากนั้นสวิตช์จะดีกว่าตัวเลือกใด ๆ

ในภาษาเชิงวัตถุนั้นมีทางเลือกอื่น ๆ ที่ใช้โครงสร้างของวัตถุได้ดีกว่า

ปัญหาเกี่ยวกับข้อความสั่ง switch เกิดขึ้นเมื่อมีการปิดสวิตช์บล็อกที่คล้ายกันหลายแห่งในหลาย ๆ ที่ในแอปพลิเคชันและต้องเพิ่มการสนับสนุนสำหรับค่าใหม่ เป็นเรื่องปกติที่นักพัฒนาจะลืมเพิ่มการสนับสนุนสำหรับค่าใหม่ให้กับหนึ่งในสวิตช์บล็อกที่กระจายอยู่รอบ ๆ แอปพลิเคชัน

ด้วย polymorphism คลาสใหม่จะแทนที่ค่าใหม่และพฤติกรรมใหม่จะถูกเพิ่มเป็นส่วนหนึ่งของการเพิ่มคลาสใหม่ พฤติกรรมที่จุดเปลี่ยนเหล่านี้จะสืบทอดมาจากซุปเปอร์คลาสแทนที่เพื่อให้มีพฤติกรรมใหม่หรือนำไปใช้เพื่อหลีกเลี่ยงข้อผิดพลาดของคอมไพเลอร์เมื่อเมธอดซูเปอร์เป็นนามธรรม

ที่ไม่มีความแตกต่างที่เห็นได้ชัดที่เกิดขึ้นก็สามารถเป็นอย่างดีมูลค่าการดำเนินการตามรูปแบบกลยุทธ์

แต่ถ้าทางเลือกของคุณคือ IF ใหญ่ ... จากนั้นอีกบล็อก ELSE ก็ลืมไปซะ


1

ใช้ภาษาที่ไม่มีคำสั่งสวิตช์ในตัว Perl 5 อยู่ในใจ

อย่างจริงจังแม้ว่าทำไมคุณต้องการที่จะหลีกเลี่ยงมันได้หรือไม่ และถ้าคุณมีเหตุผลที่ดีที่จะหลีกเลี่ยงมันทำไมไม่เพียงแค่หลีกเลี่ยงมันล่ะ?


1

พอยน์เตอร์ของฟังก์ชั่นเป็นวิธีหนึ่งในการเปลี่ยนคำสั่งสวิตช์ขนาดใหญ่ซึ่งเป็นภาษาที่คุณสามารถจับฟังก์ชั่นด้วยชื่อและทำสิ่งต่างๆ

แน่นอนว่าคุณไม่ควรบังคับให้สลับคำสั่งออกมาจากรหัสของคุณและมีโอกาสที่คุณจะทำผิดพลาดทุกครั้งซึ่งส่งผลให้เกิดโค้ดซ้ำซ้อนที่โง่เขลา (บางครั้งไม่สามารถหลีกเลี่ยงได้ แต่ภาษาที่ดีควรอนุญาตให้คุณลบความซ้ำซ้อนในขณะที่ยังคงสะอาดอยู่)

นี่คือตัวอย่างการแบ่งและพิชิตที่ยอดเยี่ยม:

สมมติว่าคุณมีล่ามบางประเภท

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

คุณสามารถใช้สิ่งนี้แทน:

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.
};

โปรดทราบว่าฉันไม่ทราบวิธีการลบความซ้ำซ้อนของ opcode_table ใน C. บางทีฉันควรถามคำถามเกี่ยวกับมัน :)


0

คำตอบที่ชัดเจนและเป็นอิสระมากที่สุดคือการใช้ชุดคำว่า 'if'

หากภาษาที่คุณใช้มีพอยน์เตอร์ของฟังก์ชัน (C) หรือมีฟังก์ชั่นที่เป็นค่าคลาสที่ 1 (Lua) คุณอาจได้ผลลัพธ์ที่คล้ายกับ "สวิทช์" โดยใช้อาเรย์ (หรือรายการ) ของ (พอยน์เตอร์)

คุณควรเจาะจงภาษามากขึ้นถ้าคุณต้องการคำตอบที่ดีกว่า


0

คำสั่ง Switch สามารถเปลี่ยนได้บ่อยครั้งด้วยการออกแบบ OO ที่ดี

ตัวอย่างเช่นคุณมีคลาสบัญชีและใช้คำสั่ง switch เพื่อทำการคำนวณที่แตกต่างกันตามประเภทของบัญชี

ฉันขอแนะนำว่าควรถูกแทนที่ด้วยคลาสบัญชีจำนวนหนึ่งซึ่งแสดงถึงประเภทบัญชีที่แตกต่างกันและการใช้อินเทอร์เฟซบัญชีทั้งหมด

สวิตช์จะไม่จำเป็นเนื่องจากคุณสามารถปฏิบัติต่อบัญชีทุกประเภทเหมือนกันและด้วยความหลากหลายรูปแบบการคำนวณที่เหมาะสมจะถูกเรียกใช้สำหรับประเภทบัญชี


0

ขึ้นอยู่กับเหตุผลที่คุณต้องการแทนที่!

ล่ามหลายคนใช้ 'gotos ที่คำนวณ' แทนคำสั่งสวิตช์สำหรับการดำเนินการ opcode

สิ่งที่ฉันพลาดเกี่ยวกับสวิตช์ C / C ++ คือ Pascal 'in' และ range ฉันยังหวังว่าฉันจะสามารถสลับสาย แต่สิ่งเหล่านี้ในขณะที่เรื่องเล็กน้อยสำหรับคอมไพเลอร์ที่จะกินเป็นงานหนักเมื่อใช้โครงสร้างและตัววนซ้ำและสิ่งต่าง ๆ ในทางตรงกันข้ามมีหลายสิ่งที่ฉันต้องการฉันสามารถแทนที่ด้วยสวิตช์หากสวิตช์ของ C () มีความยืดหยุ่นมากขึ้น!


0

สวิทช์ไม่ใช่วิธีที่ดีในการไปเพราะมันทำลายหลักการ Open Open นี่คือวิธีที่ฉันทำ

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");
   }
}

และนี่คือวิธีการใช้งาน (รับรหัสของคุณ):

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

โดยทั่วไปสิ่งที่เรากำลังทำคือการมอบหมายความรับผิดชอบให้กับชั้นเรียนของเด็กแทนการให้ผู้ปกครองตัดสินใจว่าจะทำอย่างไรกับเด็ก

คุณอาจต้องการอ่าน "หลักการทดแทน Liskov"


0

ใน JavaScript โดยใช้อาร์เรย์ที่เกี่ยวข้อง:
สิ่งนี้:

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;
    }
}

กลายเป็นสิ่งนี้:

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);
}

มารยาท


-12

โหวตให้อีกถ้า / อื่น ฉันไม่ใช่แฟนตัวยงของงบหรือการเปลี่ยนเพราะมีบางคนที่ไม่ได้ใช้ รหัสนี้สามารถอ่านได้น้อยลงหากคุณใช้เคสหรือสวิตช์ บางทีคุณอาจไม่สามารถอ่านได้น้อย แต่สำหรับผู้ที่ไม่จำเป็นต้องใช้คำสั่ง

เช่นเดียวกันกับโรงงานวัตถุ

ถ้า / บล็อกอื่นเป็นโครงสร้างที่เรียบง่ายที่ทุกคนได้รับ มีบางสิ่งที่คุณสามารถทำได้เพื่อให้แน่ใจว่าคุณจะไม่ทำให้เกิดปัญหา

ประการแรก - อย่าพยายามเยื้องข้อความหากเกินสองสามครั้ง หากคุณพบว่าตัวเองเยื้องแสดงว่าคุณทำผิด

 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

ไม่ดีจริง ๆ - ทำเช่นนี้แทน

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

การเพิ่มประสิทธิภาพถูกสาป มันไม่ได้สร้างความแตกต่างอย่างมากกับความเร็วของรหัสของคุณ

ประการที่สองฉันไม่รังเกียจที่จะแยกออกจาก If Block ตราบใดที่มีการแบ่งงบเพียงพอที่กระจัดกระจายผ่านบล็อกรหัสเฉพาะเพื่อให้ชัดเจน

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

แก้ไข : สวิตช์และทำไมฉันคิดว่ามันยากที่จะห้อมล้อม:

นี่คือตัวอย่างของคำสั่งเปลี่ยน ...

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));
 }

สำหรับฉันปัญหาที่นี่คือโครงสร้างการควบคุมปกติที่ใช้ในภาษา C เช่นภาษาได้รับความเสียหายอย่างสมบูรณ์ มีกฎทั่วไปว่าหากคุณต้องการวางโค้ดมากกว่าหนึ่งบรรทัดในโครงสร้างการควบคุมคุณใช้วงเล็บปีกกาหรือคำสั่งเริ่มต้น / สิ้นสุด

เช่น

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

สำหรับฉัน (และคุณสามารถแก้ไขฉันได้ถ้าฉันผิด) คำสั่ง Case จะโยนกฎนี้ออกไปนอกหน้าต่าง บล็อกของรหัสที่ดำเนินการตามเงื่อนไขไม่ได้อยู่ในโครงสร้างเริ่มต้น / สิ้นสุด ด้วยเหตุนี้ฉันจึงเชื่อว่า Case นั้นแตกต่างจากแนวคิดมากพอที่จะไม่ใช้

หวังว่าจะตอบคำถามของคุณ


ว้าว - เห็นได้ชัดว่าเป็นคำตอบที่ถกเถียงกัน ฉันสนใจที่จะรู้ว่าฉันทำอะไรผิด
seanyboy

เอ้อเปลี่ยนว่าซับซ้อนเกินไปเหรอ? ฉันไม่รู้ ... ดูเหมือนว่าฉันจะไม่มีคุณสมบัติด้านภาษามากมายเหลืออยู่ที่คุณสามารถใช้ได้ :) ในตัวอย่างส่วนกลางของคุณมันจะฉลาดกว่าไหมถ้า (a == 1 || a == 2 || a == 3) ทำอะไรสักอย่าง?
Lars Westergren

นอกจากนี้ในตัวอย่างสุดท้ายของคุณ "ตัวแบ่ง" จะไม่ทำอะไรในภาษาส่วนใหญ่ - ที่แยกออกจากบล็อกที่ใกล้ที่สุด (โดยปกติจะเป็นวงวน) ซึ่งในกรณีของคุณจะเกิดขึ้นต่อไปในบรรทัดถัดไป (endif) หากคุณใช้ภาษาที่แบ่งเป็น "คืน" ผลตอบแทนจำนวนมากจะขมวดคิ้วเมื่อ (ยกเว้น "คำแถลงการณ์ยาม")
Lars Westergren

4
"การเพิ่มประสิทธิภาพถูกสาปมันไม่ได้สร้างความแตกต่างอย่างมากกับความเร็วของโค้ดของคุณ" รหัสของฉันทำงานบนแพลตฟอร์มมือถือ เรื่องการปรับให้เหมาะสม นอกจากนี้สวิทช์สามารถทำให้โค้ดดูสะอาดมาก (เทียบกับถ้า...... เซลิส ..elseif..elseif ... ) หากใช้อย่างถูกต้อง ไม่เคยเห็นพวกเขา? เรียนรู้พวกเขา
Swati

Switch ไม่ซับซ้อนเลย แต่ฉันพยายามลดจำนวนการสร้างที่ใช้ในโค้ดเพื่อลดความเสียดทานในการทำความเข้าใจ
seanyboy
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.