Anahtar Üzerinden Çoklu Metotların Avantajları


12

Bugün üst düzey bir geliştiriciden "Bu arada, bir switch deyimi ile işlevleri göndermeye itirazınız nedir?" Diye bir kod incelemesi aldım. Arama yöntemlerine geçiş yoluyla bir argümanı pompalamanın ne kadar genişletilebilir değil, kötü OOP olduğu hakkında birçok yerde okudum. Ancak, gerçekten onun için kesin bir cevap bulamıyorum. Bunu kendim için bir kez ve herkes için halletmek istiyorum.

İşte rakip kod önerilerimiz (örnek olarak kullanılan php, ancak daha evrensel olarak uygulanabilir):

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

Argümanın bir kısmı, "It (switch yöntemi), varsayılan durumları ele almanın genel __call () magic yönteminde olduğundan çok daha temiz bir yoludur."

Temizlik hakkında katılmıyorum ve aslında aramayı tercih ediyorum, ama başkalarının söylediklerini duymak istiyorum.

Şemayı destekleyebileceğim argümanlar Oop:

  • Yazmanız gereken kod açısından biraz daha temiz (daha az, okunması daha kolay, dikkate alınması gereken daha az anahtar kelime)
  • Tüm eylemler tek bir yönteme devredilmemiştir. Burada yürütmede çok fazla fark yok, ama en azından metin daha bölümlere ayrılmıştır.
  • Aynı şekilde, sınıfta belirli bir nokta yerine herhangi bir yere başka bir yöntem eklenebilir.
  • Yöntemler ad boşluklu, güzel.
  • Burada geçerli değildir, ancak Switch::go()bir parametre yerine bir üye üzerinde çalıştığı bir durumu düşünün . Önce üyeyi değiştirmeniz, ardından yöntemi çağırmanız gerekir. İçin Oopherhangi bir zamanda bağımsız yöntemleri çağırabilir.

Şemayı destekleyebileceğim argümanlar Switch:

  • Tartışma uğruna, varsayılan (bilinmeyen) bir istekle başa çıkmanın daha temiz yöntemi
  • Daha az büyülü görünüyor, bu da tanıdık geliştiricileri daha rahat hissettirebilir

İki tarafın da ekleyeceği bir şey var mı? Onun için iyi bir cevap istiyorum.


@Justin Satyr Bunu düşündüm, ancak bu sorunun daha spesifik olarak kod ve en uygun çözümü bulmakla ilgili olduğunu ve bu nedenle stackoverflow için uygun olduğunu düşünüyorum. @ Yes123'ün dediği gibi, burada daha fazla insanın yanıt vermesi muhtemeldir.

__call kötü. Performansı tamamen öldürür ve bunu dışarıdan arayanlara özel olması gereken bir yöntemi çağırmak için kullanabilirsiniz.

Oopher bir yöntemi tanımlamak için bazı IDE'ler (ör. NetBeans) tarafından ayrıştırılabilen phpdoc'a sahip olmasına izin verir.
binaryLV

fluffycat.com/PHP-Design-Patterns/… .. Görünüşe göre Switch o kadar verimli değil. -1

@GordonM: Söz konusu sınıfın hiçbir özel yöntemi yoksa ne olur?
JAB

Yanıtlar:


10

Bir anahtar OOP olarak kabul edilmez, çünkü genellikle polimorfizm hile yapabilir.

Sizin durumunuzda, bir OOP uygulaması şu olabilir:

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
Çok biçimlilik doğru cevaptır. +1
Rein Henrichs

2
Bu iyidir, ancak olumsuz, kodun aşırı çoğalmasıdır. Her yöntem için bir sınıf olması gerekir ve bu başa çıkmak için daha fazla kod .. ve daha fazla bellek. Mutlu bir ortam yok mu?
Patlama Hapları

8

bu örnek için:

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

Tamam, sadece kısmen şaka yapıyorum. Bir switch deyiminin kullanımına karşı / buna karşı argüman böyle önemsiz bir örnekle güçlü bir şekilde yapılamaz, çünkü OOP tarafı sadece gönderme mekanizmasına değil, ilgili semantiğe bağlıdır .

Anahtar ifadeleri genellikle eksik sınıfların veya sınıflandırmaların bir göstergesidir, ancak zorunlu değildir. Bazen bir switch deyimi sadece bir switch deyimidir .


"Anlambilim değil, mekanizmalar" için +1
Javier

1

Belki bir cevap değil, ancak anahtar olmayan kod durumunda, bu daha iyi bir eşleşme gibi görünüyor:

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

Değerlendirmek için bulmacanın büyük bir kısmı NewSwitchveya oluşturmanız gerektiğinde ne olduğu NewOop. Programcılarınızın bir yöntemle veya diğerine göre çemberler arasında atlaması mı gerekiyor? Kurallarınız değiştiğinde vb. Ne olur?


Cevabınızı beğendim - aynı mesajı gönderecektim, ama sizin tarafınızdan ninjalandım. Ben miras korumalı yöntemlerle sorunları önlemek için method_exists is_callable () değiştirmek gerektiğini ve kodunuzu daha okunabilir hale getirmek için call_user_func $ this -> {'go _'. $ Arg} () değiştirebilirsiniz. Başka bir şey - maby sihirli yöntemi __call neden kötü - bir yorum eklemek o nesne veya nesnenin üzerinde is_callable () işlevselliğini mahveder çünkü her zaman TRUE döndürür.

Ben güncelledim is_callable, ancak call_user_func ayrıldı beri (bu sorunun bir parçası değil), geçmek için başka argümanlar olabilir. Ama şimdi bu programcılara geçti, kesinlikle @ Andrea'nın cevabının çok daha iyi olduğunu kabul ediyorum :)
Rob

0

Yürütme kodunuzu işlevlere koymak yerine tam bir komut deseni uygulayın ve her birini ortak bir arabirim uygulayan kendi sınıflarına yerleştirin. Bu yöntem, sınıfınızın farklı "durumlarını" kablolamak için IOC / DI'yi kullanmanızı sağlar ve zaman içinde kodunuza vakaları kolayca eklemenizi ve kaldırmanızı sağlar. Ayrıca, SOLID programlama ilkelerini ihlal etmeyen iyi bir kod kodu verir.


0

Bence örnek kötü. $ Argümanını kabul eden bir go () işlevi varsa, işlevde switch'i kullanmak için tamamen geçerliyse. Tekli go () işlevine sahip olmak, tüm go_where () senaryosunun davranışını değiştirmeyi kolaylaştırır. Ayrıca, sınıf arabirimini koruyacaksınız - çeşitli yöntemler kullanırsanız, sınıfın arabirimini her yeni hedefle değiştirmiş olursunuz.

Aslında anahtar, bir dizi yöntemle değil, bir sınıf kümesiyle değiştirilmelidir - bu polimorfizmdir. Sonra hedef her alt sınıf tarafından ele alınacak ve hepsi için tek bir go () yöntemi olacaktır. Şartlılığı polimorfizmle değiştirmek , Martin Fowler tarafından tanımlanan temel yeniden düzenleme işlemlerinden biridir. Ancak polimorfizme ihtiyacınız olmayabilir ve geçiş yoludur.


Arayüzü değiştirmezseniz ve bir alt sınıfın kendi uygulaması go()varsa, Liskov İkame ilkesini kolayca ihlal edebilir. Eğer daha fazla işlevsellik için ekliyorsanız go(), sen mi ben concerend olduğum kadarıyla olarak arayüzünü değiştirmek istiyorum.
Patlama Hapları
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.