Sử dụng phương thức constructor hay setter?


16

Tôi đang làm việc với một mã UI nơi tôi có một Actionlớp, đại loại như thế này -

public class MyAction extends Action {
    public MyAction() {
        setText("My Action Text");
        setToolTip("My Action Tool tip");
        setImage("Some Image");
    }
}

Khi lớp Hành động này được tạo, có khá nhiều giả định rằng Actionlớp sẽ không thể tùy chỉnh (theo một nghĩa nào đó - văn bản, chú giải công cụ hoặc hình ảnh của nó sẽ không bị thay đổi ở bất kỳ đâu trong mã). Bây giờ, chúng tôi cần thay đổi văn bản hành động tại một số vị trí trong mã. Vì vậy, tôi đã đề nghị đồng nghiệp của mình xóa văn bản hành động được mã hóa cứng khỏi hàm tạo và chấp nhận nó làm đối số, để mọi người buộc phải truyền văn bản hành động. Một cái gì đó giống như mã này dưới đây -

public class MyAction extends Action {
    public MyAction(String actionText) {
        setText(actionText);
        setTooltip("My Action tool tip"); 
        setImage("My Image"); 
    }
}

Tuy nhiên, ông nghĩ rằng vì setText()phương thức thuộc về lớp cơ sở nên nó có thể được sử dụng linh hoạt để truyền văn bản hành động bất cứ nơi nào thể hiện hành động được tạo. Theo cách đó, không cần phải thay đổi MyActionlớp hiện có . Vì vậy, mã của anh ấy sẽ trông giống như thế này.

MyAction action = new MyAction(); //this creates action instance with the hardcoded text
action.setText("User required new action text"); //overwrite the existing text.

Tôi không chắc chắn nếu đó là một cách chính xác để đối phó với vấn đề. Tôi nghĩ rằng trong trường hợp người dùng đã đề cập ở trên dù sao cũng sẽ thay đổi văn bản, vậy tại sao không ép buộc anh ta trong khi xây dựng hành động? Lợi ích duy nhất tôi thấy với mã gốc là người dùng có thể tạo lớp Hành động mà không cần suy nghĩ nhiều về việc thiết lập văn bản.


1
Ngôn ngữ bạn đang sử dụng không cho phép quá tải các nhà xây dựng?
Mat

1
Tôi đang sử dụng Java. Có, nó cho phép và tôi nghĩ đó có thể là một cách để giải quyết vấn đề này
zswap

2
Tôi sẽ lưu ý rằng nếu bạn không có cách nào để thiết lập các thành viên của lớp sau khi thực tế, lớp của bạn thực sự không thay đổi . Bằng cách cho phép một trình thiết lập công khai, giờ đây lớp của bạn trở nên có thể thay đổi và bạn có thể cần cân nhắc điều này nếu bạn phụ thuộc vào tính bất biến.
cbojar

Tôi sẽ nói rằng nếu nó cần được đặt để đối tượng của bạn hợp lệ, hãy đặt nó vào mọi hàm tạo ... nếu nó là tùy chọn (có mặc định hợp lý) và bạn không quan tâm đến tính bất biến, hãy đặt nó vào một setter. Không thể khởi tạo đối tượng của bạn sang trạng thái không hợp lệ hoặc sau khi khởi tạo đưa nó vào trạng thái không hợp lệ bất cứ khi nào có thể.
Bill K

Câu trả lời:


15

Lợi ích duy nhất tôi thấy với mã gốc là người dùng có thể tạo lớp Hành động mà không cần suy nghĩ nhiều về việc thiết lập văn bản.

Đó thực sự không phải là một lợi ích, đối với hầu hết các mục đích, đó là một nhược điểm và trong các trường hợp còn lại tôi gọi đó là cà vạt. Điều gì xảy ra nếu ai đó quên gọi setText () sau khi xây dựng? Điều gì xảy ra nếu đó là trường hợp trong một số trường hợp bất thường, có lẽ là một trình xử lý lỗi? Nếu bạn muốn thực sự buộc văn bản được đặt, bạn cần buộc nó vào thời gian biên dịch, vì chỉ các lỗi thời gian biên dịch mới thực sự gây tử vong . Bất cứ điều gì xảy ra trong thời gian chạy phụ thuộc vào đường dẫn mã cụ thể đó đang được thực thi.

Tôi thấy hai con đường rõ ràng phía trước:

  1. Sử dụng một tham số constructor, như bạn đề xuất. Nếu bạn thực sự muốn, bạn có thể vượt qua nullhoặc một chuỗi trống, nhưng thực tế là bạn không chỉ định một văn bản rõ ràng hơn là ẩn. Thật dễ dàng để thấy sự tồn tại của một nulltham số và thấy rằng có lẽ có một số ý nghĩ được đặt vào nó, nhưng không dễ để thấy việc thiếu một cuộc gọi phương thức và xác định xem việc thiếu đó có phải là cố ý hay không. Đối với một trường hợp đơn giản như thế này, đây có lẽ là cách tiếp cận tôi sẽ thực hiện.
  2. Sử dụng một mô hình nhà máy / xây dựng. Điều này có thể quá mức cần thiết cho một kịch bản đơn giản như vậy, nhưng trong trường hợp tổng quát hơn, nó rất linh hoạt vì nó cho phép bạn đặt bất kỳ số lượng tham số nào và kiểm tra các điều kiện tiên quyết trước hoặc trong khi khởi tạo đối tượng (nếu xây dựng đối tượng là một hoạt động lớn và / hoặc lớp có thể được sử dụng theo nhiều cách, đây có thể là một lợi thế rất lớn ). Đặc biệt trong Java nó cũng là một thành ngữ phổ biến và việc tuân theo các mẫu đã được thiết lập trong ngôn ngữ và khung mà bạn đang sử dụng rất hiếm khi là một điều xấu.

10

Quá tải nhà xây dựng sẽ là một giải pháp đơn giản và dễ hiểu ở đây:

public class MyAction extends Action {
    public MyAction(String actionText) {
        setText(actionText);
        setTooltip("My Action tool tip"); 
        setImage("My Image"); 
    }
    public MyAction() {
        this("My Action Text");
    }
}

Nó là tốt hơn so với gọi .setTextsau, bởi vì cách này không có gì cần phải ghi đè, actionTextcó thể là điều dự định ngay từ đầu.

Khi mã của bạn phát triển và bạn sẽ cần linh hoạt hơn nữa (điều này chắc chắn sẽ xảy ra), bạn sẽ được hưởng lợi từ mô hình nhà máy / nhà xây dựng được đề xuất bởi một câu trả lời khác.


Điều gì xảy ra khi họ muốn tùy chỉnh một tài sản thứ hai?
kevin cline

3
Đối với thuộc tính thứ 2, thứ 3, .., bạn có thể áp dụng kỹ thuật tương tự, nhưng càng nhiều thuộc tính bạn muốn tùy chỉnh, điều này càng trở nên khó sử dụng. Tại một số điểm, sẽ có ý nghĩa hơn khi thực hiện mô hình nhà máy / nhà xây dựng, cũng như @ michael-kjorling nói trong câu trả lời của mình.
janos

6

Thêm một phương thức 'setText' trôi chảy:

public class MyAction ... {
  ...
  public MyAction setText(String text) { ... ; return this; }
}

MyAction a = new MyAction().setText("xxx");

Điều gì có thể rõ ràng hơn thế? Nếu bạn quyết định thêm một tài sản tùy biến khác, không có vấn đề.


+1, tôi đồng ý và thêm một câu trả lời khác bổ sung cho nhiều thuộc tính hơn. Tôi nghĩ rằng API thông thạo sẽ dễ hiểu hơn khi bạn có nhiều hơn 1 thuộc tính làm ví dụ.
Machado

Tôi thích giao diện lưu loát, đặc biệt là cho các nhà xây dựng sản xuất các vật thể bất biến! Càng nhiều thông số, công việc này càng tốt. Nhưng nhìn vào ví dụ cụ thể trong câu hỏi này, tôi đoán rằng nó setText()được định nghĩa trên lớp Hành động mà MyAction thừa hưởng. Nó có khả năng đã có một loại trả về void.
GlenPeterson

1

Giống như kevin cline đã nói trong câu trả lời của mình, tôi nghĩ rằng cách để đi là tạo ra một API trôi chảy . Tôi chỉ muốn nói thêm rằng API thông thạo hoạt động tốt hơn khi bạn có nhiều tài sản bạn có thể sử dụng.

Nó sẽ làm cho mã của bạn dễ đọc hơn, và theo quan điểm của tôi dễ dàng hơn và, aham , "gợi cảm" để viết.

Trong trường hợp của bạn, nó sẽ như thế này (xin lỗi cho bất kỳ lỗi đánh máy nào, đã một năm kể từ khi tôi viết chương trình java cuối cùng của mình):

 public class MyAction extends Action {
    private String _text     = "";
    private String _tooltip  = "";
    private String _imageUrl = "";

    public MyAction()
    {
       // nothing to do here.
    }

    public MyAction text(string value)
    {
       this._text = value;
       return this;
    }

    public MyAction tooltip(string value)
    {
       this._tooltip = value;
       return this;
    }

    public MyAction image(string value)
    {
       this._imageUrl = value;
       return this;
    }
}

Và cách sử dụng sẽ như thế này:

MyAction action = new MyAction()
    .text("My Action Text")
    .tooltip("My Action Tool tip")
    .image("Some Image");

Ý tưởng tồi, nếu họ quên đặt văn bản hoặc bất kỳ điều gì quan trọng.
Prakhar

1

Lời khuyên để sử dụng các nhà xây dựng hoặc nhà xây dựng nói chung là tốt, nhưng, theo kinh nghiệm của tôi, bỏ lỡ một số điểm chính cho Hành động, trong đó

  1. Có thể cần phải được quốc tế hóa
  2. Có khả năng tiếp thị sẽ thay đổi vào phút cuối.

Tôi thực sự khuyên bạn nên đọc tên, chú giải công cụ, biểu tượng, v.v ... từ tệp thuộc tính, XML, v.v. Ví dụ, đối với hành động Mở tệp, bạn có thể chuyển vào Thuộc tính và nó sẽ tìm

File.open.name=Open
File.open.tooltip=Open a file
File.open.icon=somedir/open.jpg

Đây là một định dạng khá dễ dịch sang tiếng Pháp, để thử một biểu tượng mới tốt hơn, v.v. Không cần thời gian lập trình viên hoặc biên dịch lại.

Đây chỉ là một phác thảo sơ bộ, còn lại rất nhiều cho người đọc ... Hãy tìm các ví dụ khác về quốc tế hóa.


0

Sẽ vô ích khi gọi setText (actionText) hoặc setTooltip ("Mẹo công cụ hành động của tôi") bên trong hàm tạo; nó dễ dàng hơn (và bạn đạt được hiệu suất cao hơn) nếu bạn chỉ cần khởi tạo trực tiếp trường tương ứng:

    public MyAction(String actionText) {
        this.actionText = actionText;
    }

Nếu bạn thay đổi actionText trong suốt thời gian tồn tại của đối tượng tương ứng MyAction, bạn nên đặt một phương thức setter; nếu không khởi tạo trường chỉ trong hàm tạo mà không cung cấp phương thức setter.

Vì tooltip và hình ảnh là hằng số, coi chúng là hằng số; có các lĩnh vực:

private (or even public) final static String TOOLTIP = "My Action Tooltip";

Trên thực tế, khi thiết kế các đối tượng phổ biến (không phải là đậu hoặc các đối tượng đại diện cho cấu trúc dữ liệu nghiêm ngặt), việc cung cấp setters và getters là một ý tưởng tồi, vì chúng là loại đóng gói phá vỡ.


4
Bất kỳ trình biên dịch và JITter có khả năng nửa chừng nào cũng nên thực hiện các lệnh gọi setText (), v.v. hơn không.
một CVn

0

Tôi nghĩ điều này đúng nếu chúng ta sẽ tạo một lớp hành động chung (như cập nhật, được sử dụng để cập nhật Nhân viên, Bộ ...). Tất cả phụ thuộc vào kịch bản. Nếu một lớp hành động cụ thể (như nhân viên cập nhật) (được sử dụng nhiều nơi trong ứng dụng - Nhân viên cập nhật) được tạo ra với cường độ để giữ cùng một văn bản, chú giải công cụ và hình ảnh ở mọi nơi trong ứng dụng (cho quan điểm nhất quán). Vì vậy, mã hóa cứng có thể được thực hiện cho văn bản, chú giải công cụ và hình ảnh để cung cấp văn bản, chú giải công cụ và hình ảnh mặc định. Vẫn để linh hoạt hơn, để tùy chỉnh chúng, cần có các phương thức setter tương ứng. Giữ trong tâm trí chỉ 10% những nơi chúng ta phải thay đổi nó. Lấy văn bản hành động mỗi lần từ người dùng có thể gây ra văn bản khác nhau mỗi lần cho cùng một hành động. Giống như 'Cập nhật Emp', 'Cập nhật nhân viên', 'Thay đổi nhân viên' hoặc 'Chỉnh sửa nhân viên'.


Tôi nghĩ rằng nhà xây dựng quá tải vẫn nên giải quyết vấn đề. Bởi vì trong tất cả các trường hợp "10%", trước tiên bạn sẽ tạo một Hành động với văn bản mặc định và sau đó thay đổi văn bản hành động bằng phương thức "setText ()". Tại sao không đặt văn bản phù hợp trong khi xây dựng hành động.
zswap

0

Hãy nghĩ về cách các thể hiện sẽ được sử dụng và sử dụng một giải pháp sẽ hướng dẫn, hoặc thậm chí bắt buộc người dùng sử dụng các thể hiện đó theo cách phù hợp, hoặc ít nhất là tốt nhất. Một lập trình viên sử dụng lớp này sẽ có rất nhiều thứ khác để lo lắng và suy nghĩ. Lớp này không nên thêm vào danh sách.

Ví dụ, nếu lớp MyAction được coi là bất biến sau khi xây dựng (và có thể là khởi tạo khác), thì nó không nên có một phương thức setter. Nếu hầu hết thời gian nó sẽ sử dụng "Văn bản hành động của tôi" mặc định, cần có một hàm tạo không tham số, cộng với một hàm tạo cho phép một văn bản tùy chọn. Bây giờ người dùng không cần phải suy nghĩ để sử dụng lớp đúng 90% thời gian. Nếu người dùng thường phải đưa ra một số suy nghĩ cho văn bản, hãy bỏ qua hàm tạo không tham số. Bây giờ người dùng buộc phải suy nghĩ khi cần thiết và không thể bỏ qua một bước cần thiết.

Nếu một MyActionthể hiện cần phải có thể thay đổi sau khi xây dựng đầy đủ thì bạn cần một trình thiết lập cho văn bản. Thật hấp dẫn khi bỏ qua việc đặt giá trị trong hàm tạo (nguyên tắc DRY - "Đừng lặp lại chính mình") và, nếu giá trị mặc định thường đủ tốt thì tôi sẽ làm. Nhưng nếu không, yêu cầu văn bản trong hàm tạo buộc người dùng phải suy nghĩ khi nào họ nên.

Lưu ý rằng những người dùng này không bị câm . Họ chỉ có quá nhiều vấn đề thực sự phải lo lắng. Bằng cách suy nghĩ về "giao diện" của lớp, bạn cũng có thể ngăn nó trở thành một vấn đề thực sự - và một vấn đề không cần thiết.


0

Trong giải pháp đề xuất sau đây, siêu lớp là trừu tượng và có cả ba thành viên được đặt thành một giá trị mặc định.

Lớp con có các hàm tạo khác nhau để lập trình viên có thể khởi tạo nó.

Nếu hàm tạo đầu tiên được sử dụng, tất cả các thành viên sẽ có các giá trị mặc định.

Nếu hàm tạo thứ hai được sử dụng, bạn cung cấp một giá trị ban đầu cho thành viên actionText để lại hai thành viên khác có giá trị mặc định ...

Nếu hàm tạo thứ ba được sử dụng, bạn khởi tạo nó với một giá trị mới cho actionText và toolTip, để lại imageURl với giá trị mặc định ...

Và như thế.

public abstract class Action {
    protected String text = "Default action text";
    protected String toolTip = "Default action tool tip";
    protected String imageURl = "http://myserver.com/images/default.png";

    .... rest of code, I guess setters and getters
}

public class MyAction extends Action {


    public MyAction() {

    }

    public MyAction(String actionText) {
        setText(actionText);
    }

    public MyAction(String actionText, String toolTip_) {
        setText(actionText);
        setToolTip(toolTip_);   
    }

    public MyAction(String actionText, String toolTip_; String imageURL_) {
        setText(actionText);
        setToolTip(toolTip_);
        setImageURL(imageURL_);
    }


}
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.