Keuntungan untuk Berbagai Metode dibandingkan Beralih

12

Saya menerima tinjauan kode dari pengembang senior hari ini dengan bertanya, "Ngomong-ngomong, apa keberatan Anda untuk mengirim fungsi dengan menggunakan pernyataan switch?" Saya telah membaca di banyak tempat tentang bagaimana memompa argumen melalui metode switch to call adalah OOP yang buruk, tidak dapat diperluas, dll. Namun, saya tidak dapat benar-benar memberikan jawaban yang pasti untuknya. Saya ingin menyelesaikan ini untuk diri saya sendiri sekali dan untuk semua.

Berikut adalah saran kode kami yang bersaing (php digunakan sebagai contoh, tetapi dapat diterapkan secara lebih universal):

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

Bagian dari argumennya adalah "Ini (beralih metode) memiliki cara yang jauh lebih bersih dalam menangani kasus default daripada apa yang Anda miliki dalam metode sihir __call () generik."

Saya tidak setuju tentang kebersihan dan bahkan lebih suka menelepon, tetapi saya ingin mendengar apa yang orang lain katakan.

Argumen yang bisa saya kemukakan dalam mendukung Oopskema:

  • Sedikit lebih bersih dalam hal kode yang harus Anda tulis (lebih sedikit, lebih mudah dibaca, lebih sedikit kata kunci untuk dipertimbangkan)
  • Tidak semua tindakan didelegasikan ke metode tunggal. Tidak banyak perbedaan dalam eksekusi di sini, tetapi setidaknya teks lebih terkotak.
  • Dalam nada yang sama, metode lain dapat ditambahkan di mana saja di kelas daripada tempat tertentu.
  • Metode diberi nama, yang bagus.
  • Tidak berlaku di sini, tetapi mempertimbangkan kasus di mana Switch::go()dioperasikan pada anggota daripada parameter. Anda harus mengubah anggota terlebih dahulu, lalu memanggil metode. Untuk OopAnda dapat memanggil metode secara mandiri kapan saja.

Argumen yang bisa saya kemukakan dalam mendukung Switchskema:

  • Demi argumen, metode yang lebih bersih untuk menangani permintaan default (tidak diketahui)
  • Tampaknya kurang ajaib, yang mungkin membuat pengembang asing merasa lebih nyaman

Adakah yang punya sesuatu untuk ditambahkan di kedua sisi? Saya ingin memiliki jawaban yang bagus untuknya.

Pil Ledakan
sumber
@ Justin Satyr Saya memikirkan hal itu, tapi saya pikir pertanyaan ini lebih khusus tentang kode dan menemukan solusi optimal dan dengan demikian sesuai untuk stackoverflow. Dan seperti yang dikatakan @ yes123, lebih banyak orang cenderung merespons di sini.
__call buruk. Ini benar-benar membunuh kinerja, dan Anda dapat menggunakannya untuk memanggil metode yang seharusnya pribadi untuk penelepon luar.
Oopmemungkinkan untuk memiliki phpdoc untuk menggambarkan setiap metode, yang dapat diuraikan oleh beberapa IDE (misalnya, NetBeans).
binaryLV
fluffycat.com/PHP-Design-Patterns/… .. rupanya Switch tidak terlalu efisien. -1
@GordonM: Bagaimana jika kelas yang bersangkutan tidak memiliki metode pribadi?
JAB

Jawaban:

10

Saklar dianggap bukan OOP karena sering polimorfisme dapat melakukan trik.

Dalam kasus Anda, implementasi OOP bisa seperti ini:

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();
Ando
sumber
1
Polimorfisme adalah jawaban yang benar. +1
Rein Henrichs
2
Ini bagus, tetapi sisi buruknya adalah proliferasi kode yang berlebihan. Anda harus memiliki kelas untuk setiap metode dan itu lebih banyak kode untuk menangani .. dan lebih banyak memori. Apakah tidak ada media yang bahagia?
Pil Ledakan
8

untuk contoh ini:

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

OKE, hanya bercanda sebagian di sini. Argumen untuk / terhadap penggunaan pernyataan switch tidak dapat dengan kuat dibuat dengan contoh sepele seperti itu, karena sisi OOP tergantung pada semantik yang terlibat, bukan hanya mekanisme pengiriman .

Pernyataan pergantian sering merupakan indikasi kelas yang hilang atau klasifikasi, tetapi tidak harus. Terkadang pernyataan switch hanyalah pernyataan switch .

Steven A. Lowe
sumber
+1 untuk "semantik, bukan mekanisme"
Javier
1

Mungkin bukan jawaban, tetapi dalam kasus kode non-switch, sepertinya ini akan menjadi pasangan yang lebih baik:

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

Sebagian besar teka-teki untuk menilai adalah apa yang terjadi ketika Anda perlu membuat NewSwitchatau NewOop. Apakah programmer Anda harus melewati rintangan dengan satu metode atau yang lain? Apa yang terjadi ketika aturan Anda berubah, dll.

rampok
sumber
Saya suka jawaban Anda - akan memposting yang sama, tetapi mendapatkan ninja'ed oleh Anda. Saya pikir Anda harus mengubah method_exists menjadi is_callable () untuk menghindari masalah dengan metode yang dilindungi yang diwariskan dan Anda dapat mengubah call_user_func menjadi $ this -> {'go _'. $ Arg} () untuk membuat kode Anda lebih mudah dibaca. Hal lain - maby menambahkan komentar mengapa metode ajaib __call buruk - ia merusak fungsionalitas is_callable () pada objek atau instance itu karena ia akan selalu mengembalikan TRUE.
Saya memperbarui ke is_callable, tetapi meninggalkan call_user_func karena (bukan bagian dari pertanyaan ini), mungkin ada argumen lain untuk diteruskan. Tapi sekarang ini telah pindah ke programmer, saya pasti setuju bahwa jawaban @ Andrea jauh lebih baik :)
Rob
0

Alih-alih hanya menempatkan kode eksekusi Anda ke dalam fungsi mengimplementasikan pola perintah penuh dan menempatkan masing-masing di kelas mereka sendiri yang mengimplementasikan antarmuka umum. Metode ini memungkinkan Anda untuk menggunakan IOC / DI untuk menghubungkan berbagai "kasus" kelas Anda dan memungkinkan Anda untuk dengan mudah menambah dan menghapus kasus dari kode Anda dari waktu ke waktu. Ini juga memberi Anda sedikit kode yang tidak melanggar prinsip pemrograman SOLID.

P. Roe
sumber
0

Saya pikir contohnya buruk. Jika ada fungsi go () yang menerima argumen $ where, if valid untuk menggunakan switch di function. Memiliki fungsi single go () memudahkan untuk memodifikasi perilaku semua go_where () scenarious. Selain itu, Anda akan mempertahankan antarmuka kelas - jika Anda menggunakan berbagai metode, Anda memodifikasi antarmuka kelas dengan setiap tujuan baru.

Sebenarnya switch tidak boleh diganti dengan set metode, tetapi dengan set kelas - ini adalah polimorfisme. Kemudian tujuan akan ditangani oleh setiap subclass, dan akan ada metode single go () untuk mereka semua. Ganti kondisional dengan polimorfisme adalah salah satu refactoring dasar, yang dijelaskan oleh Martin Fowler. Tapi Anda mungkin tidak perlu polimorfisme, dan beralih adalah cara untuk pergi.

Maxim Krizhanovsky
sumber
Jika Anda tidak mengubah antarmuka dan subkelas memiliki penerapannya sendiri, go()ia dapat dengan mudah melanggar prinsip Pergantian Liskov. Jika Anda menambahkan fungsionalitas lebih ke go(), Anda jangan ingin mengubah antarmuka sejauh yang saya concerend.
Pil Ledakan