ข้อดีของวิธีการหลายวิธีมากกว่าสวิตช์


12

ฉันได้รับการตรวจสอบโค้ดจากผู้พัฒนาระดับสูงในวันนี้โดยถามว่า "อย่างไรก็ตามอะไรคือสิ่งที่คุณคัดค้านการมอบหมายหน้าที่โดยใช้คำสั่งสวิตช์?" ฉันได้อ่านในหลาย ๆ ที่เกี่ยวกับวิธีการปั๊มการโต้เถียงผ่านวิธีสลับการโทรเป็นวิธีการที่ไม่ดี OOP ไม่สามารถขยายได้ ฯลฯ อย่างไรก็ตามฉันไม่สามารถหาคำตอบที่ชัดเจนสำหรับเขาได้ ฉันต้องการจะทำสิ่งนี้ให้กับตัวเองซักครั้ง

นี่คือคำแนะนำเกี่ยวกับรหัสการแข่งขันของเรา (php ใช้เป็นตัวอย่าง แต่สามารถใช้ได้ในระดับสากล):

class Switch {
   public function go($arg) {
      switch ($arg) {
         case "one":
            echo "one\n";
         break;
         case "two":
            echo "two\n";
         break;
         case "three":
            echo "three\n";
         break;
         default:
            throw new Exception("Unknown call: $arg");
         break;
      }
   }
}

class Oop {
   public function go_one() {
      echo "one\n";
   }
   public function go_two() {
      echo "two\n";
   }
   public function go_three() {
      echo "three\n";
   }
   public function __call($_, $__) {
      throw new Exception("Unknown call $_ with arguments: " . print_r($__, true));
   }
}

ส่วนหนึ่งของการโต้แย้งของเขาคือ "มัน (วิธีการเปลี่ยน) มีวิธีที่สะอาดกว่ามากในการจัดการกรณีเริ่มต้นกว่าสิ่งที่คุณมีในวิธีการทั่วไป __call () มายากล"

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

ข้อโต้แย้งที่ฉันจะได้รับจากการสนับสนุนของOopโครงการ:

  • คำศัพท์ที่คุณต้องเขียนให้สะอาดยิ่งขึ้น (น้อยกว่าง่ายต่อการอ่านและคำหลักที่ต้องพิจารณาน้อยลง)
  • ไม่ใช่ทุกการกระทำที่มอบหมายให้กับวิธีการเดียว ไม่แตกต่างกันมากนักในการดำเนินการที่นี่ แต่อย่างน้อยข้อความจะถูกแบ่งออกมากขึ้น
  • ในหลอดเลือดดำเดียวกันสามารถเพิ่มวิธีอื่นได้ทุกที่ในชั้นเรียนแทนที่จะเป็นจุดเฉพาะ
  • วิธีการคือ namespaced ซึ่งเป็นสิ่งที่ดี
  • ไม่ได้ใช้ที่นี่ แต่ให้พิจารณากรณีและปัญหาที่Switch::go()ดำเนินการกับสมาชิกแทนที่จะเป็นพารามิเตอร์ คุณจะต้องเปลี่ยนสมาชิกก่อนจากนั้นเรียกวิธีการ สำหรับOopคุณสามารถโทรหาวิธีการได้อย่างอิสระตลอดเวลา

ข้อโต้แย้งที่ฉันจะได้รับจากการสนับสนุนของSwitchโครงการ:

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

ใครมีอะไรเพิ่มสำหรับทั้งสองด้าน? ฉันต้องการคำตอบที่ดีสำหรับเขา


@ จัสติน Satyr ฉันคิดเกี่ยวกับเรื่องนี้ แต่ฉันคิดว่าคำถามนี้เฉพาะเจาะจงมากขึ้นเกี่ยวกับโค้ดและการหาทางออกที่ดีที่สุดและเหมาะสมสำหรับการโอเวอร์โฟลว์ และตามที่ @ yes123 พูดว่ามีคนอีกมากที่จะตอบคำถามที่นี่

__ โทรไม่ดี มันฆ่าประสิทธิภาพได้อย่างสมบูรณ์และคุณสามารถใช้วิธีนี้เพื่อเรียกวิธีการที่ควรเป็นแบบส่วนตัวกับผู้โทรเข้าภายนอก

Oopอนุญาตให้ phpdoc อธิบายแต่ละวิธีซึ่งสามารถแยกวิเคราะห์โดย IDE บางตัว (เช่น NetBeans)
binaryLV

fluffycat.com/PHP-Design-Patterns/ … .. เห็นได้ชัดว่า Switch ไม่ใช่สิ่งที่มีประสิทธิภาพ -1

@GordonM: ถ้าชั้นเรียนที่สงสัยไม่มีวิธีส่วนตัวล่ะ
JAB

คำตอบ:


10

สวิตช์นั้นไม่ถือว่าเป็น OOP เนื่องจากบ่อยครั้งที่ความหลากหลายสามารถทำกลอุบายได้

ในกรณีของคุณการใช้งาน OOP อาจเป็นเช่นนี้:

class Oop 
{
  protected $goer;

  public function __construct($goer)
  {
    $this->goer = $goer;
  }

  public function go()
  {
    return $this->goer->go();
  }
}

class Goer
{
  public function go()
  {
    //...
  }
}

class GoerA extends Goer
{
  public function go()
  {
    //...
  }
}

class GoerB extends Goer
{
  public function go()
  {
    //...
  }
}

class GoerC extends Goer
{
  public function go()
  {
    //...
  }
}


$oop = new Oop(new GoerB());
$oop->go();

1
ความแตกต่างคือคำตอบที่ถูกต้อง +1
Rein Henrichs

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

8

สำหรับตัวอย่างนี้:

class Switch
{
    public function go($arg)
    {
        echo "$arg\n";
    }
}

ตกลงล้อเล่นที่นี่บางส่วนเท่านั้น อาร์กิวเมนต์สำหรับ / ต่อต้านการใช้งบเปลี่ยนไม่สามารถทำอย่างมากด้วยเช่นตัวอย่างเล็ก ๆ น้อย ๆ เพราะด้าน OOP ขึ้นอยู่กับความหมายที่เกี่ยวข้องกับการไม่ได้เป็นเพียงกลไกการจัดส่ง

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


+1 สำหรับ "ซีแมนทิกส์ไม่ใช่กลไก"
Javier

1

อาจไม่ใช่คำตอบ แต่ในกรณีที่ไม่ใช่รหัสสวิตช์ดูเหมือนว่านี่จะเป็นการจับคู่ที่ดีกว่า:

class Oop {
  /**
   * User calls $oop->go('one') then this function will determine if the class has a 
   * method 'go_one' and call that. If it doesn't, then you get your error.
   * 
   * Subclasses of Oop can either overwrite the existing methods or add new ones.
   */
  public function go($arg){

    if(is_callable(array($this, 'go_'. $arg))){
      return call_user_func(array($this, 'go_'. $arg));
    }

    throw new Exception("Unknown call: $arg");
  }

  public function go_one() {
    echo "one\n";
  }
  public function go_two() {
    echo "two\n";
  }
  public function go_three() {
    echo "three\n";
  }
}

ส่วนใหญ่ของปริศนาที่จะประเมินคือสิ่งที่เกิดขึ้นเมื่อคุณจำเป็นต้องสร้างหรือNewSwitch NewOopโปรแกรมเมอร์ของคุณต้องกระโดดผ่านห่วงโดยวิธีหนึ่งหรืออื่น ๆ ? จะเกิดอะไรขึ้นเมื่อกฎของคุณเปลี่ยน ฯลฯ


ฉันชอบคำตอบของคุณ - จะโพสต์สิ่งเดียวกัน แต่ได้นินจาจากคุณ ฉันคิดว่าคุณควรเปลี่ยน method_exists เป็น is_callable () เพื่อหลีกเลี่ยงปัญหาเกี่ยวกับวิธีการป้องกันที่สืบทอดมาและคุณสามารถเปลี่ยน call_user_func เป็น $ this -> {'go _'. $ arg} () เพื่อทำให้โค้ดของคุณอ่านง่ายขึ้น สิ่งอื่น - maby เพิ่มความคิดเห็นว่าทำไมเมธอด __call นั้นไม่ดี - มันคือฟังก์ชั่นที่หักล้าง is_callable () บนวัตถุหรืออินสแตนซ์ของมันเพราะมันจะคืนค่า TRUE เสมอ

ฉันอัปเดตเป็นis_callableแต่ทิ้ง call_user_func ตั้งแต่ (ไม่ใช่ส่วนหนึ่งของคำถามนี้) อาจมีข้อโต้แย้งอื่น ๆ ที่จะต้องทำตาม แต่ตอนนี้ที่นี้ได้ย้ายไปโปรแกรมเมอร์แน่นอนผมเห็นว่า @ คำตอบของ Andrea ดีมาก :)
ร็อบ

0

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


0

ฉันคิดว่าตัวอย่างไม่ดี หากมีฟังก์ชั่น go () ที่ยอมรับการโต้แย้ง $ ที่ไหนถ้าเป็นที่สมบูรณ์แบบที่จะใช้สวิตช์ในฟังก์ชั่น การมีฟังก์ชั่น go () เพียงตัวเดียวทำให้การปรับเปลี่ยนพฤติกรรมของ go_where () ทั้งหมดเป็นเรื่องง่ายขึ้น นอกจากนี้คุณจะยังคงรักษาอินเทอร์เฟซของคลาส - ถ้าคุณใช้วิธีการต่าง ๆ คุณกำลังปรับเปลี่ยนอินเทอร์เฟซของคลาสกับแต่ละปลายทางใหม่

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


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