Đó là cách tốt hơn để gọi một phương thức chỉ có sẵn cho một lớp thực hiện một giao diện chứ không phải một phương thức khác?


11

Về cơ bản tôi cần phải thực hiện các hành động khác nhau với một điều kiện nhất định. Mã hiện tại được viết theo cách này

Giao diện cơ sở

// DoSomething.java
interface DoSomething {

   void letDoIt(String info);
}

Thực hiện lớp công nhân đầu tiên

class DoItThisWay implements DoSomething {
  ...
}

Thực hiện lớp công nhân thứ hai

class DoItThatWay implements DoSomething {
   ...
}

Lớp chính

   class Main {
     public doingIt(String info) {
        DoSomething worker;
        if (info == 'this') {
          worker = new DoItThisWay();
        } else {
          worker = new DoItThatWay();
        }
        worker.letDoIt(info)
     }

Mã này hoạt động tốt và dễ hiểu.

Bây giờ, do một yêu cầu mới, tôi cần phải truyền một mẩu thông tin mới chỉ có ý nghĩa DoItThisWay.

Câu hỏi của tôi là: phong cách mã hóa sau có tốt để xử lý yêu cầu này không.

Sử dụng biến và phương thức lớp mới

// Use new class variable and method

class DoItThisWay implements DoSomething {
  private int quality;
  DoSomething() {
    quality = 0;
  }

  public void setQuality(int quality) {
    this.quality = quality;
  };

 public void letDoIt(String info) {
   if (quality > 50) { // make use of the new information
     ...
   } else {
     ...
   }
 } ;

}

Nếu tôi làm theo cách này, tôi cần thực hiện thay đổi tương ứng cho người gọi:

   class Main {
     public doingIt(String info) {
        DoSomething worker;
        if (info == 'this') {
          int quality = obtainQualityInfo();
          DoItThisWay tmp = new DoItThisWay();
          tmp.setQuality(quality)
          worker = tmp;

        } else {
          worker = new DoItThatWay();
        }
        worker.letDoIt(info)
     }

Đó có phải là một phong cách mã hóa tốt? Hoặc tôi có thể bỏ nó

   class Main {
     public doingIt(String info) {
        DoSomething worker;
        if (info == 'this') {
          int quality = obtainQualityInfo();
          worker = new DoItThisWay();
          ((DoItThisWay) worker).setQuality(quality)
        } else {
          worker = new DoItThatWay();
        }
        worker.letDoIt(info)
     }

19
Tại sao không chỉ truyền qualityvào các nhà xây dựng cho DoItThisWay?
David Arno

Tôi có lẽ cần phải xem lại câu hỏi của mình ... vì lý do hiệu suất, việc xây dựng DoItThisWayDoItThatWayđược thực hiện một lần trong hàm tạo của Main. Mainlà một lớp sống lâu dài và doingItđược gọi nhiều lần.
Anthony Kong

Vâng, cập nhật câu hỏi của bạn sẽ là một ý tưởng tốt trong trường hợp đó.
David Arno

Bạn có nghĩa là để nói rằng setQualityphương thức sẽ được gọi nhiều lần trong suốt vòng đời của DoItThisWayđối tượng?
jpmc26

1
Không có sự khác biệt có liên quan giữa hai phiên bản bạn đã trình bày. Từ quan điểm logic, họ làm điều tương tự.
Sebastian Redl

Câu trả lời:


6

Tôi cho rằng qualitycần phải được đặt cùng với mỗi letDoIt()cuộc gọi trên a DoItThisWay().

Vấn đề tôi thấy phát sinh ở đây là: Bạn đang giới thiệu khớp nối tạm thời (nghĩa là điều gì xảy ra nếu bạn quên gọi setQuality()trước khi gọi letDoIt()a DoItThisWay?). Và các triển khai cho DoItThisWayDoItThatWayđang chuyển hướng (một cái cần phải setQuality()được gọi, cái kia thì không).

Mặc dù điều này có thể không gây ra vấn đề ngay bây giờ, cuối cùng nó có thể quay lại ám ảnh bạn. Có thể đáng để xem xét lại letDoIt()và xem xét nếu thông tin chất lượng có thể cần phải là một phần của việc infobạn vượt qua nó; nhưng điều này phụ thuộc vào các chi tiết.


15

Đó có phải là một phong cách mã hóa tốt?

Theo quan điểm của tôi, cả hai phiên bản của bạn đều không. Đầu tiên phải gọi setQualitytrước letDoItcó thể được gọi là khớp nối tạm thời . Bạn bị mắc kẹt khi xem DoItThisWaynhư là một công cụ phái sinh DoSomething, nhưng nó không (ít nhất là không hoạt động), nó giống như một

interface DoSomethingWithQuality {
   void letDoIt(String info, int quality);
}

Điều này sẽ làm cho Mainmột cái gì đó như

class Main {
    // omitting the creation
    private DoSomething doSomething;
    private DoSomethingWithQuality doSomethingWithQuality;

    public doingIt(String info) {
        DoSomething worker;
        if (info == 'this') {
            int quality = obtainQualityInfo();
            doSomethingWithQuality.letDoIt(info, quality);
        } else {
            doSomething.letDoIt(info);
        }
    }
}

Mặt khác, bạn có thể truyền trực tiếp các tham số cho các lớp (giả sử điều này là có thể) và ủy thác quyết định sử dụng cái nào cho nhà máy (điều này sẽ làm cho các thể hiện có thể hoán đổi cho nhau và cả hai đều có thể xuất phát từ đó DoSomething). Điều này sẽ làm Mainnhư thế này

class Main {
    private DoSomethingFactory doSomethingFactory;

     public doingIt(String info) {
         int quality = obtainQualityInfo();
         DoSomething doSomethingWorker = doSomethingFactory.Create(info, quality);
         doSomethingWorker.letDoIt();
     }
}

Tôi biết rằng bạn đã viết rằng

bởi vì lý do hiệu năng, việc xây dựng DoIt ThisWay và DoItThatWay được thực hiện một lần trong hàm tạo của Main

Nhưng bạn có thể lưu trữ các bộ phận tốn kém để tạo trong nhà máy và chuyển chúng cho nhà xây dựng.


Argh, bạn đang theo dõi tôi trong khi tôi gõ câu trả lời? Chúng tôi thực hiện những điểm tương tự như vậy (nhưng của bạn là toàn diện và hùng hồn hơn nhiều);)
CharonX

1
@DocBrown Vâng, điều đó gây tranh cãi, nhưng vì DoItThisWayyêu cầu chất lượng phải được đặt ra trước khi gọi letDoItnó hoạt động khác đi. Tôi mong muốn DoSomethinghoạt động theo cùng một cách (không đặt chất lượng trước đó) cho tất cả các công cụ phái sinh. Điều này có nghĩa không? Tất nhiên đây là loại mờ, vì nếu chúng ta gọi setQualitytừ phương thức nhà máy, khách hàng sẽ không biết về chất lượng có phải được đặt hay không.
Paul Kertscher

2
@DocBrown Tôi cho rằng hợp đồng letDoIt()là (thiếu bất kỳ thông tin bổ sung nào) "Tôi có thể thực hiện chính xác (làm bất cứ điều gì tôi làm) nếu bạn cung cấp cho tôi String info". Đòi hỏi rằng setQuality()được gọi là trước tăng cường điều kiện tiên quyết của việc gọi letDoIt(), và do đó sẽ là một sự vi phạm của LSP.
CharonX

2
@CharonX: nếu, chẳng hạn, sẽ có một hợp đồng ngụ ý rằng " letDoIt" sẽ không đưa ra bất kỳ ngoại lệ nào và quên gọi "setQuality" sẽ tạo ra letDoItmột ngoại lệ, sau đó tôi đồng ý, việc triển khai như vậy sẽ vi phạm LSP. Nhưng những điều đó trông giống như những giả định rất giả tạo với tôi.
Doc Brown

1
Đây là một câu trả lời tốt. Điều duy nhất tôi muốn thêm là một số bình luận về việc khớp nối tạm thời tồi tệ đến mức nào đối với một cơ sở mã. Đó là một hợp đồng ẩn giữa các thành phần dường như tách rời. Với khớp nối bình thường, ít nhất nó rõ ràng và dễ nhận biết. Làm điều này về cơ bản là đào một cái hố và phủ nó bằng lá.
JimmyJames

10

Giả sử qualitykhông thể truyền vào hàm tạo và setQualityyêu cầu gọi đến .

Hiện tại, một đoạn mã như

    int quality = obtainQualityInfo();
    worker = new DoItThisWay();
    ((DoItThisWay) worker).setQuality(quality);

là quá nhỏ để đầu tư quá nhiều suy nghĩ vào nó. IMHO trông hơi xấu xí, nhưng không thực sự khó hiểu.

Các vấn đề phát sinh khi một đoạn mã như vậy phát triển và do tái cấu trúc, bạn kết thúc với một cái gì đó như

    int quality = obtainQualityInfo();
    worker = CreateAWorkerForThisInfo();
    ((DoItThisWay) worker).setQuality(quality);

Bây giờ, bạn không thấy ngay lập tức nếu mã đó vẫn đúng và trình biên dịch sẽ không cho bạn biết. Vì vậy, giới thiệu một biến tạm thời của loại chính xác và tránh diễn viên, sẽ an toàn hơn một chút về loại, mà không cần bất kỳ nỗ lực thực sự nào.

Tuy nhiên, tôi thực sự sẽ cho tmpmột cái tên tốt hơn:

      int quality = obtainQualityInfo();
      DoItThisWay workerThisWay = new DoItThisWay();
      workerThisWay.setQuality(quality)
      worker = workerThisWay;

Việc đặt tên như vậy giúp làm cho mã sai nhìn sai.


tmp thậm chí có thể được đặt tên tốt hơn một cái gì đó như "workerThatUsesQuality"
user949300

1
@ user949300: IMHO đó không phải là một ý tưởng hay: giả sử DoItThisWaycó các tham số cụ thể hơn - theo những gì bạn đề xuất, tên cần phải được thay đổi mỗi lần. Tốt hơn là sử dụng tên biến vor làm cho loại đối tượng rõ ràng, không phải tên phương thức nào có sẵn.
Doc Brown

Để làm rõ, nó sẽ là tên của biến, không phải tên của lớp. Tôi đồng ý rằng đặt tất cả các khả năng trong tên lớp là một ý tưởng tồi. Vì vậy, tôi đề nghị workerThisWaytrở thành một cái gì đó như thế usingQuality. Trong đoạn mã nhỏ đó sẽ an toàn hơn để được cụ thể hơn. (Và, nếu có một cụm từ tốt hơn trong miền của họ hơn là "usedQuality", hãy sử dụng nó.
user949300

2

Một giao diện phổ biến trong đó việc khởi tạo thay đổi dựa trên dữ liệu thời gian chạy thường cho chính mẫu nhà máy.

DoThingsFactory factory = new DoThingsFactory(thingThatProvidesQuality);
DoSomething doSomething = factory.getDoer(info);

doSomething.letDoIt();

Sau đó, DoThingsFactory có thể lo lắng về việc nhận và thiết lập thông tin chất lượng bên trong getDoer(info)phương thức trước khi trả lại đối tượng DoThingsWithQuality cụ thể cho giao diện DoS Something.

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.