Làm thế nào để khái niệm về một lớp thay đổi khi truyền dữ liệu đến hàm tạo thay vì các tham số của phương thức?


12

Hãy nói rằng chúng tôi đang làm một trình phân tích cú pháp. Một cách thực hiện có thể là:

public sealed class Parser1
{
    public string Parse(string text)
    {
       ...
    }
}

Hoặc chúng ta có thể chuyển văn bản cho hàm tạo thay thế:

public sealed class Parser2
{
    public Parser2(string text)
    {
       this.text = text;
    }

    public string Parse()
    {
       ...
    }
}

Cách sử dụng là đơn giản trong cả hai trường hợp, nhưng nó có nghĩa gì khi cho phép nhập tham số Parser1, so với cái khác? Tôi đã gửi thông điệp gì cho một lập trình viên đồng nghiệp, khi họ xem API? Ngoài ra, có bất kỳ lợi thế / bất lợi kỹ thuật trong các trường hợp nhất định?

Một câu hỏi khác xuất hiện khi tôi nhận ra rằng một giao diện sẽ khá vô nghĩa trong lần thực hiện thứ hai:

public interface IParser
{
    string Parse();
}

... Trong đó một giao diện trên giao diện đầu tiên có thể phục vụ ít nhất một số mục đích. Điều đó có nghĩa gì đặc biệt, rằng một lớp có "có thể can thiệp" hay không?


Đây là lần đầu tiên tôi thấy một số câu hỏi và trả lời hữu ích về cách ngữ nghĩa của OO thực sự thể hiện ý định. Cho đến bây giờ tất cả chỉ là cú pháp đường, với tôi.

Câu trả lời:


11

Về mặt ngữ nghĩa, trong OOP, bạn chỉ nên truyền cho hàm tạo một tập các tham số cần thiết để xây dựng lớp - tương tự như khi bạn gọi một phương thức, bạn chỉ nên truyền cho nó các tham số cần để thực hiện logic nghiệp vụ của nó.

Các tham số mà bạn cần truyền trong hàm tạo là các tham số không có giá trị mặc định hợp lý và nếu lớp của bạn là bất biến (hoặc thực sự là a struct) thì các thuộc tính không mặc định phải được thông qua.

Về hai ví dụ của bạn:

  • nếu bạn chuyển textđến hàm tạo, nó gợi ý rằng Parser2lớp sẽ được xây dựng cụ thể để phân tích cú pháp văn bản đó sau đó. Nó sẽ là một trình phân tích cú pháp cụ thể. Đây thường là trường hợp khi xây dựng lớp rất tốn kém hoặc tinh tế, một RegEx có thể được biên dịch trong hàm tạo, vì vậy một khi bạn giữ một cá thể, bạn có thể sử dụng lại nó mà không phải trả chi phí biên dịch; một ví dụ khác là khởi tạo PRNG - tốt hơn nếu nó được thực hiện hiếm khi.
  • nếu bạn chuyển textđến phương thức, nó báo hiệu Parser1có thể được sử dụng lại để phân tích các văn bản khác nhau bằng các cuộc gọi.

3
Tôi muốn thêm rằng có một tín hiệu yếu, mà Parser1 duy trì trạng thái - tức là một chuỗi văn bản cụ thể có thể tạo ra các kết quả khác nhau tùy thuộc vào trường hợp đã được thực hiện trước đây. Điều này không nhất thiết phải như vậy, nhưng nó có thể.
jmoreno

8

Chà, hãy nhớ lại ý nghĩa của việc truyền một biến dưới dạng tham số của hàm tạo: Bạn khởi tạo một đối tượng để sử dụng các biến đối tượng của nó trong các phương thức của đối tượng. Vấn đề là bạn có thể muốn sử dụng nó trong nhiều hơn một phương thức vì bạn muốn có sự gắn kết cao trong lớp học của mình.

Truyền tham số trực tiếp đến một phương thức có nghĩa là theo cách gửi tin nhắn đến một đối tượng và có thể nhận được câu trả lời. Do đó, khách hàng muốn đối tượng cung cấp dịch vụ cho anh ta.

Vì vậy, cuối cùng, đó là hai phương tiện truyền tham số rất khác nhau và bạn nên chọn liệu đối tượng của mình sẽ cung cấp dịch vụ hay cung cấp một số chức năng vốn có trong khi quản lý một số thông tin bên trong.


+1. Sự gắn kết là cách tôi quyết định liệu nó thuộc về phương thức hay hàm tạo.
jhewlett

4

Cách sử dụng rất đơn giản trong cả hai trường hợp, nhưng việc cho phép nhập tham số vào Parser1 có nghĩa là gì?

Đó là một sự thay đổi thiết kế cơ bản. Và thiết kế nên truyền đạt ý định và ý nghĩa. Bạn có cần phải có các đối tượng riêng biệt cho mỗi chuỗi bạn muốn phân tích không? Nói cách khác, tại sao chúng ta cần một phiên bản của trình phân tích cú pháp với chuỗiX và một phiên bản khác với chuỗiY? Điều gì về parse (ing) và chuỗi đã cho mà hai người phải sống và chết cùng nhau? Giả sử rằng việc thực hiện "phân tích cú pháp] cơ bản" (như Robert Harvey nói) không thay đổi, dường như không có điểm nào. Và thậm chí sau đó IMHO nghi vấn của nó.

Làm thế nào để khái niệm về một lớp thay đổi khi truyền dữ liệu đến hàm tạo thay vì các tham số của phương thức?

Các tham số của hàm tạo cho tôi biết những điều này là bắt buộc đối với một đối tượng. Nhà nước thích hợp không được đảm bảo mà không có họ. Ngoài ra, tôi biết làm thế nào / tại sao một trình phân tích cú pháp khác về cơ bản với một trình phân tích cú pháp khác.

Các tham số của hàm tạo giúp tôi không phải biết quá nhiều về cách sử dụng lớp. Nếu thay vào đó tôi phải thiết lập một số thuộc tính nhất định - làm sao tôi biết điều đó? Cả một lon giun mở ra. Tính chất gì? Theo thứ tự? Trước khi tôi dùng phương pháp gì? và như thế.

Một câu hỏi khác xuất hiện khi tôi nhận ra rằng một giao diện sẽ khá vô nghĩa trong lần thực hiện thứ hai:

Một giao diện, như trong API, là các phương thức và thuộc tính được hiển thị với mã máy khách. Đừng bao bọc trong public interface { ... }độc quyền. Vì vậy, ý nghĩa của giao diện là trong một trong hai-hay constructor vs tham số phương pháp tiến thoái lưỡng nan, KHÔNG public interface Iparservspublic sealed class Parser

Các sealedlớp học là số lẻ. Nếu tôi đang nghĩ về các triển khai trình phân tích cú pháp khác nhau - bạn đã đề cập đến "Iparser" - thì kế thừa là suy nghĩ đầu tiên của tôi. Nó chỉ là một phần mở rộng khái niệm tự nhiên trong suy nghĩ của tôi. IE tất cả các ParserXs về cơ bản Parserlà s. Làm thế nào khác để nói điều đó? ... Một con chó chăn cừu Đức là một con chó (thừa kế), nhưng tôi có thể huấn luyện con vẹt của mình sủa (hành động như một con chó - "giao diện"); nhưng Polly không phải là một con chó, chỉ giả vờ, đã học được một tập hợp con chó. Các lớp, trừu tượng hoặc cách khác, phục vụ hoàn hảo như giao diện .


Nếu nó đi như một trình phân tích cú pháp và nói như một trình phân tích cú pháp, thì đó là ... một con vịt!

2

Phiên bản thứ hai của lớp có thể được làm bất biến.

Giao diện vẫn có thể được sử dụng để cung cấp khả năng trao đổi thực hiện cơ bản.


Nó không thể được làm cho bất biến trong phiên bản đầu tiên, bằng cách truyền dữ liệu xung quanh trong lớp theo cách chức năng?
ciscoheat

2
Chắc chắn rồi. Nhưng mô hình chung của tính bất biến là thiết lập các thành viên lớp bằng cách sử dụng hàm tạo và có các thuộc tính chỉ đọc. Để lập trình chức năng, bạn thậm chí không cần một lớp.
Robert Harvey

Scylla và Charybdis: tôi chọn OO hoặc dữ liệu bất biến? Tôi chưa bao giờ nghe nó đặt theo cách đó trước đây.

2

Trình phân tích cú pháp1

Xây dựng với một hàm tạo mặc định và chuyển văn bản đầu vào vào một phương thức ngụ ý rằng Parser1 có thể sử dụng lại được.

Trình phân tích cú pháp2

Truyền văn bản đầu vào vào hàm tạo hàm ý rằng Parser2 mới phải được tạo cho mỗi chuỗi đầu vào.


Chắc chắn, vì vậy nếu chúng ta đưa nó lên cấp độ tiếp theo, những gì để kết luận về một đối tượng có thể sử dụng lại so với không thể tái sử dụng?
ciscoheat

Tôi sẽ không giả định nhiều hơn thế, thay vào đó là trì hoãn tài liệu.
Mike Partridge

Một "đối tượng có thể sử dụng lại" trong đó danh tính của nó thay đổi là một trình tự không tuần tự. Và với các khung được quản lý có nghĩa là bạn thậm chí không phải vứt bỏ mọi thứ, chỉ cần ghi đè lên chúng hoặc đưa chúng ra khỏi phạm vi, điều đó chắc chắn là không cần thiết. Xây dựng đi!
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.