2009-05-14 31 views
6

Vì vậy, tôi đã vào một cuộc tranh luận thân thiện với đồng nghiệp qua một đoạn mã:Tôi có nên sử dụng một lĩnh vực hoặc tài sản trong lớp để thiết lập giá trị

public sealed class NewObject 
{ 
    private string _stuff = string.Empty; 

    public string Stuff 
    { 
     get { return GetAllStuff(); } 
    } 

    private string GetAllStuff() 
    { 
     //Heavy string manipulation of _stuff 
    } 

    public NewObject(string stuffToStartWith) 
    { 
     _stuff = stuffToStartWith; 
    } 

    public static NewObject operator +(NewObject obj1, NewObject obj2) 
    { 
     if (obj1 == null) 
      throw new ArgumentNullException(); 

     if (obj2 == null) 
      throw new ArgumentNullException(); 

     NewObject result = new NewObject(string.Empty); 
     result._stuff = String.Concat(obj1._stuff, obj2._stuff); 

     return result; 
    } 
} 

Đối số đã kết thúc ghi đè điều hành. Đồng nghiệp của tôi cảm thấy rằng đó không phải là thực hành lập trình tốt nhất để đặt giá trị của các trường riêng tư ở bất kỳ đâu ngoài hàm tạo. Giải pháp do đồng nghiệp của tôi đề xuất là cấu trúc lại tên của thuộc tính Stuff thành AllStuff và thêm thuộc tính, Stuff, có truy cập get AND set và sử dụng thuộc tính Stuff mới trong ghi đè toán tử. Làm như sau:

public static NewObject operator +(NewObject obj1, NewObject obj2) 
    { 
     if (obj1 == null) 
      throw new ArgumentNullException(); 

     if (obj2 == null) 
      throw new ArgumentNullException(); 

     NewObject result = new NewObject(string.Empty); 
     result.Stuff = String.Concat(obj1.Stuff, obj2.Stuff); 

     return result; 
    } 

Tôi không đồng ý. Tôi cảm thấy cách đầu tiên tốt hơn vì nó giữ tài sản chỉ đọc bên ngoài lớp học. Câu hỏi của tôi là, cách nào là cách thực hành tốt nhất cho thiết kế hướng đối tượng?

Trả lời

7

Bạn có thể tự cung cấp cho mình private set trên thuộc tính (sẽ giữ lại mức hiển thị hoặc thiếu trong khi cho phép bạn sử dụng cú pháp thuộc tính), nhưng điều đó không thực sự giải quyết vấn đề.

Trong lớp học, tôi nói rằng các biến là trò chơi công bằng. Bất kỳ nơi nào bên ngoài, kể cả các lớp được kế thừa, phải là getset thuộc tính, nhưng trong lớp khai báo, tôi cho rằng sẽ được phép gán thành viên riêng tư.

2

Bạn đang phải

err ... để xây dựng, các biến riêng tư của bạn là của bạn. Nếu ai đó thực hiện một thao tác trên bạn mà thay đổi giá trị của đối tượng, (đặc biệt là một cái gì đó như +), không có gì sai với việc sửa đổi giá trị bên ngoài của hàm tạo. Đó là toàn bộ quan điểm của họ là riêng tư.

Trừ khi bạn muốn nó bất biến ...

Cập nhật
Càng tôi nghĩ về nó, tôi càng tin rằng đồng nghiệp của bạn là khó hiểu biến 'tin' với những 'liên tục' - hoặc có lẽ hợp nhất hai khái niệm. Không có lý do gì mà các biến riêng tư phải giữ nguyên trong suốt cuộc đời của đối tượng, đó là điều mà bạn của bạn có vẻ ngụ ý. const là không thay đổi, riêng tư chỉ dành cho đối tượng, chúng là hai mẫu rất khác biệt.

Update2
Ngoài ra, thiết kế của ông sụp đổ nếu đột nhiên đối tượng của bạn có nhiều hơn chỉ là một chuỗi - và các biến được đan xen vào nhau (suy nghĩ của một đối tượng chuỗi, mà có một char * và len, và phải duy trì với nhau). Điều cuối cùng bạn muốn là cho người dùng phải đối phó với các biến nội bộ của một đối tượng. Để đối tượng là một đối tượng và duy trì các giá trị nội bộ của riêng nó và trình bày một thực thể duy nhất cho người dùng.

+0

@cyber: Tôi nghĩ đây là vấn đề của OP không hoàn toàn rõ ràng. Nó nghe giống tôi như đồng nghiệp của anh ta đã ủng hộ việc không thay đổi một biến riêng tư trực tiếp bên ngoài của nhà xây dựng hoặc việc thực hiện tài sản. Tôi không nghĩ anh ta đang ủng hộ việc giữ các giá trị như nhau cho cuộc đời của vật thể. –

1

Tôi không thấy lợi ích của cách tiếp cận của anh ấy là gì.

0

Tôi personaly thích không có lĩnh vực nào cả, vì vậy tôi sử dụng tính năng tự động thực hiện private tính thay vì private lĩnh vực và public-getprivate-set tính nếu muốn có public thuộc tính read-only.
Nếu tôi phải thêm mã vào thuộc tính, tôi vẫn chỉ sử dụng trường bên trong của trình truy cập thuộc tính và sử dụng getters và setters ở mọi nơi khác bao gồm hàm tạo.
Tôi cũng phải sử dụng các trường, nếu tôi cần các trường readonly, nhưng C# 4.0 sẽ giới thiệu các thuộc tính chỉ đọc.


Hơn nữa, tôi sẽ tránh được toàn bộ sự cố bằng cách sử dụng mã sau đây.

public static NewObject operator +(NewObject obj1, NewObject obj2) 
{ 
    return new NewObject(String.Concat(obj1.Stuff, obj2.Stuff)); 
} 

thực ưa thích của tôi sẽ là một cái gì đó như thế này.

public sealed class NewObject 
{ 
    private String Stuff { get; set; } 

    // Use a method instead of a property because the operation is heavy.  
    public String GetAllStuff() 
    { 
     // Heavy string manipulation of this.Stuff. 
     return this.Stuff; 
    } 

    // Or lets use a property because this.GetAllStuff() is not to heavy. 
    public String AllStuff 
    { 
     get { return this.GetAllStuff(); } 
    } 

    public NewObject(String stuffToStartWith) 
    { 
     this.Stuff = stuffToStartWith; 
    } 

    public static NewObject operator +(NewObject obj1, NewObject obj2) 
    { 
     // Error handling goes here. 

     return new NewObject(String.Concat(obj1.Stuff, obj2.Stuff); 
    } 
} 
+0

Tôi có hiểu lầm ý bạn là "chỉ đọc thuộc tính" không? Ngay bây giờ tôi có thể mã một tài sản mà không có một setter, làm cho nó chỉ đọc ... –

2

Vấn đề chung phải làm với chính sách hợp đồng.

Khái niệm về thuộc tính (tập hợp công khai) là khi nó được gọi, các hành động khác có thể được thực hiện ngoài khái niệm ngữ nghĩa của trạng thái thay đổi. Ví dụ, gọi một setter có thể kích hoạt các sự kiện, kích hoạt một thiết bị ngoại vi và vân vân.

Đồng nghiệp của bạn đang nói rằng bằng cách không sử dụng thuộc tính, bạn đang tiến hành hợp đồng và không có sự kiện nào bị sa thải.

Vì vậy, đây là bạn nên làm theo quan điểm của đồng nghiệp của bạn về quan điểm:

this.Prop = CalculateSomeValue(); 
if (this.Prop < kPropMin) { 
    this.Prop = kPropMin; 
} 
else if (this.Prop > kPropMax * 2) { 
    this.Prop = kPropMax * 2; 
} 
this.Prop = this.Prop/2; 

Bây giờ, đây là một trường hợp giả tạo, nhưng tôi đã chỉ cần nhấn một tài sản nặng có thể lên đến ba lần trong get và lên đến ba lần trong tập hợp, và một trong số đó có thể là bất hợp pháp (thiết lập để kHighLimit/2). Tôi có thể làm việc xung quanh điều này bằng cách sử dụng một địa phương và gọi thiết lập chính xác một lần ở cuối. Mặc dù tôi chỉ muốn gây rối với lĩnh vực này.

Tôi nghĩ cách tiếp cận tốt hơn là thực hiện nó: sử dụng thuộc tính bên trong lớp của bạn nếu và chỉ nếu bạn muốn gọi tất cả các tác dụng phụ của tập hợp hoặc nhận, nếu không tuân theo tinh thần của tài sản thay thế.

- rõ - Bằng cách tuân theo tinh thần của tài sản, chúng ta hãy nói rằng sở hữu bộ của tôi trông như thế này:

bool PropValueOutOfRange(int val) { 
    return val < kPropMin || val > kPropMax; 
} 

public int Prop { 
    set { 
     if (PropValueOutOfRange(value)) 
      throw new ArgumentOutOfRangeException("value"); 
     if (PropValueConflictsWithInternalState(value)) 
      throw new ArgumentException("value"); 
     _prop = value; 
     NotifyPeriperalOfPropChange(_prop); 
     FirePropChangedEvent(/* whatever args might be needed */); 
    } 
} 

Trong này tôi đã yếu tố ra rất nhiều các chi tiết mang phong cách grunge, nhưng cho phép tôi sử dụng lại chúng. Vì vậy, bây giờ tôi cảm thấy tự tin khi chạm vào trường riêng tư _prop vì tôi có cùng cơ sở hạ tầng để đảm bảo rằng tôi giữ nó trong phạm vi và thông báo cho thiết bị ngoại vi và kích hoạt sự kiện.

này cho phép tôi viết mã này:

_prop = CalculateSomeValue(); 

if (_prop < kPropMin) 
    _prop = kPropMin; 
else if (_prop > kPropMax * 2) 
    _prop = kPropMax; 

_prop /= 2; 

NotifyPeripheralOfPropChange(); 
FirePropChangedEvent(); 

Tôi đang sử dụng các công cụ tương tự như những người sử dụng để xây dựng tài sản vì vậy tôi đang làm việc trong tinh thần của bất động sản. Tôi duy trì phạm vi chính xác (nhưng không ném - tôi biết rõ hơn, tôi là người thực hiện), nhấn các sự kiện ngoại vi và hỏa hoạn, và tôi làm điều đó một cách chu đáo, dễ đọc và hiệu quả - không bừa bãi.

+0

1 bởi vì sau khi đọc này tôi nghĩ rằng đây có lẽ là quá trình suy nghĩ của đồng nghiệp của tôi. Tuy nhiên với mục đích của mã đang bị tranh chấp, tài sản sẽ không bao giờ được thay đổi bên ngoài lớp vì bất kỳ lý do gì. –

+0

Câu hỏi: Trong phương pháp thực dụng của bạn, khi bạn nói 'tuân theo tinh thần của tài sản' bạn có nghĩa là nếu tài sản không bao giờ thay đổi bên ngoài lớp thì hãy sử dụng trường để cập nhật như tôi đang làm ở trên? –

Các vấn đề liên quan