Ưu điểm cho nhiều phương thức chuyển đổi


12

Tôi đã nhận được một đánh giá mã từ một nhà phát triển cao cấp ngày hôm nay hỏi "Nhân tiện, sự phản đối của bạn đối với việc gửi các chức năng bằng cách tuyên bố chuyển đổi là gì?" Tôi đã đọc ở nhiều nơi về cách bơm một đối số thông qua chuyển sang các phương thức gọi là OOP xấu, không thể mở rộng, v.v. Tuy nhiên, tôi thực sự không thể đưa ra một câu trả lời dứt khoát cho anh ta. Tôi muốn giải quyết điều này cho bản thân mình một lần và mãi mãi.

Dưới đây là các đề xuất mã cạnh tranh của chúng tôi (php được sử dụng làm ví dụ, nhưng có thể áp dụng phổ biến hơn):

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

Một phần trong lập luận của ông là "Nó (phương thức chuyển đổi) có cách xử lý các trường hợp mặc định gọn gàng hơn nhiều so với những gì bạn có trong phương thức ma thuật __call () chung chung."

Tôi không đồng ý về sự sạch sẽ và thực tế thích gọi điện, nhưng tôi muốn nghe những gì người khác nói.

Đối số tôi có thể đưa ra để hỗ trợ Oopchương trình:

  • Sạch hơn một chút về mã bạn phải viết (ít hơn, dễ đọc hơn, ít từ khóa hơn để xem xét)
  • Không phải tất cả các hành động được ủy thác cho một phương thức duy nhất. Không có nhiều sự khác biệt trong thực thi ở đây, nhưng ít nhất văn bản có nhiều ngăn hơn.
  • Trong cùng một hướng, một phương thức khác có thể được thêm vào bất cứ nơi nào trong lớp thay vì một vị trí cụ thể.
  • Các phương thức được đặt tên, đó là tốt đẹp.
  • Không áp dụng ở đây, nhưng xem xét một trường hợp Switch::go()hoạt động trên một thành viên chứ không phải là một tham số. Bạn sẽ phải thay đổi thành viên trước, sau đó gọi phương thức. Đối với Oopbạn có thể gọi các phương thức độc lập bất cứ lúc nào.

Đối số tôi có thể đưa ra để hỗ trợ Switchchương trình:

  • Để tranh luận, phương pháp sạch hơn để xử lý một yêu cầu mặc định (không xác định)
  • Có vẻ ít ma thuật hơn, điều này có thể khiến các nhà phát triển xa lạ cảm thấy thoải mái hơn

Bất cứ ai có bất cứ điều gì để thêm cho cả hai bên? Tôi muốn có một câu trả lời tốt cho anh ấy.


@Justin Satyr Tôi đã nghĩ về điều đó, nhưng tôi nghĩ rằng câu hỏi này cụ thể hơn về mã và tìm một giải pháp tối ưu và do đó thích hợp cho stackoverflow. Và như @ yes123 nói, nhiều người có khả năng trả lời ở đây.

__call là xấu. Nó hoàn toàn giết chết hiệu suất và bạn có thể sử dụng nó để gọi một phương thức được cho là riêng tư đối với người gọi bên ngoài.

Oopcho phép có phpdoc để mô tả từng phương thức, có thể được phân tích cú pháp bởi một số IDE (ví dụ: NetBeans).
binaryLV

fluffycat.com/PHP-Design-Potypes/ ' .. dường như Switch không hiệu quả lắm. -1

@GordonM: Điều gì xảy ra nếu lớp trong câu hỏi không có phương thức riêng tư?
JAB

Câu trả lời:


10

Một chuyển đổi được coi là không OOP vì thường đa hình có thể thực hiện thủ thuật.

Trong trường hợp của bạn, việc triển khai OOP có thể là:

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
Đa hình là câu trả lời đúng. +1
Rein Henrichs

2
Điều này là tốt, nhưng nhược điểm là sự phát triển quá mức của mã. Bạn phải có một lớp cho mỗi phương thức và đó là nhiều mã hơn để xử lý .. và nhiều bộ nhớ hơn. Có phải không có một phương tiện hạnh phúc?
Thuốc nổ

8

cho ví dụ này:

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

OK, chỉ đùa một phần ở đây. Đối số / không sử dụng câu lệnh chuyển đổi có thể được đưa ra mạnh mẽ với một ví dụ tầm thường như vậy, bởi vì phía OOP phụ thuộc vào ngữ nghĩa liên quan, không chỉ đơn thuần là cơ chế điều phối .

Các câu lệnh chuyển đổi thường là một dấu hiệu của các lớp hoặc phân loại bị thiếu, nhưng không nhất thiết phải như vậy. Đôi khi một câu lệnh switch chỉ là một câu lệnh switch .


+1 cho "ngữ nghĩa, không phải cơ chế"
Javier

1

Có lẽ không phải là một câu trả lời, nhưng trong trường hợp mã không chuyển đổi, có vẻ như đây sẽ là một kết hợp tốt hơn:

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

Một phần lớn của câu đố để đánh giá là những gì xảy ra khi bạn cần tạo NewSwitchhoặc NewOop. Các lập trình viên của bạn có phải nhảy qua vòng bằng phương pháp này hay phương pháp khác không? Điều gì xảy ra khi quy tắc của bạn thay đổi, v.v.


Tôi thích câu trả lời của bạn - cũng sẽ đăng bài tương tự, nhưng đã bị ninja đánh bại. Tôi nghĩ bạn nên thay đổi method_exists thành is_callable () để tránh các vấn đề với các phương thức được bảo vệ kế thừa và bạn có thể thay đổi call_user_func thành $ this -> {'go _'. $ Arg} () để làm cho mã của bạn dễ đọc hơn. Một điều nữa - maby thêm một nhận xét tại sao phương thức ma thuật __call lại tệ - nó phá hỏng chức năng is_callable () trên đối tượng hoặc thể hiện của nó bởi vì nó sẽ luôn trả về ĐÚNG.

Tôi đã cập nhật is_callable, nhưng để lại call_user_func vì (không phải là một phần của câu hỏi này), có thể có các đối số khác được đưa ra. Nhưng bây giờ điều này đã chuyển sang các lập trình viên, tôi chắc chắn đồng ý rằng câu trả lời của @ Andrea tốt hơn nhiều :)
Rob

0

Thay vì chỉ đưa mã thực thi của bạn vào các hàm, hãy thực hiện một mẫu lệnh đầy đủ và đặt từng mã trong lớp riêng của chúng thực hiện một giao diện chung. Phương pháp này cho phép bạn sử dụng IOC / DI để kết nối các "trường hợp" khác nhau của lớp và cho phép bạn dễ dàng thêm và xóa các trường hợp khỏi mã của mình theo thời gian. Nó cũng cung cấp cho bạn một chút mã tốt mà không vi phạm các nguyên tắc lập trình RẮN.


0

Tôi nghĩ rằng ví dụ là xấu. Nếu có một hàm go () chấp nhận đối số $ ở đâu, nếu hoàn toàn hợp lệ để sử dụng chuyển đổi trong hàm. Có chức năng go () duy nhất giúp dễ dàng sửa đổi hành vi của tất cả go_where () phối cảnh. Ngoài ra, bạn sẽ duy trì giao diện lớp - nếu bạn sử dụng nhiều phương thức khác nhau, bạn đang sửa đổi giao diện của lớp với mỗi đích mới.

Trên thực tế, chuyển đổi không nên được thay thế bằng tập hợp các phương thức, nhưng với tập hợp các lớp - đây là đa hình. Sau đó, đích sẽ được xử lý bởi mỗi lớp con và sẽ có phương thức go () cho tất cả chúng. Thay thế điều kiện bằng đa hình là một trong những phép tái cấu trúc cơ bản, được mô tả bởi Martin Fowler. Nhưng bạn có thể không cần đa hình, và chuyển đổi là cách để đi.


Nếu bạn không thay đổi giao diện và một lớp con có triển khai riêng go()thì nó có thể dễ dàng vi phạm nguyên tắc Thay thế Liskov. Nếu bạn đang thêm nhiều chức năng hơn để go()bạn làm muốn thay đổi giao diện như xa như tôi concerend.
Thuốc nổ
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.