2012-09-06 21 views
17

Vì nhiều lý do khác nhau, tôi muốn bắt đầu sử dụng nhiều loại không thay đổi trong thiết kế. Tại thời điểm này, tôi đang làm việc với một dự án trong đó có một lớp học hiện như thế này:Thiết kế không thể thay đổi: Đối phó với sự gây khó chịu của người xây dựng

public class IssueRecord 
{ 
    // The real class has more readable names :) 
    public string Foo { get; set; } 
    public string Bar { get; set; } 
    public int Baz { get; set; } 
    public string Prop { get; set; } 
    public string Prop2 { get; set; } 
    public string Prop3 { get; set; } 
    public string Prop4 { get; set; } 
    public string Prop5 { get; set; } 
    public string Prop6 { get; set; } 
    public string Prop7 { get; set; } 
    public string Prop8 { get; set; } 
    public string Prop9 { get; set; } 
    public string PropA { get; set; } 
} 

Lớp này đại diện cho một số định dạng trên đĩa mà thực sự không có nhiều tài sản này, vì vậy refactoring nó vào bit nhỏ là khá nhiều câu hỏi tại thời điểm này.

Điều này có nghĩa là hàm tạo trên lớp này thực sự cần phải có 13 tham số trong một thiết kế không thay đổi? Nếu không, tôi có thể thực hiện các bước nào để giảm số lượng tham số được chấp nhận trong hàm tạo nếu tôi làm thiết kế này không thay đổi được?

+0

Có điều gì đó bên ngoài lớp cần đặt giá trị hay chính lớp đó có thể đặt giá trị? Nếu lớp có thể làm điều đó, thì bạn có thể có một hàm tạo parameterless. –

+1

Xem: http://stackoverflow.com/questions/355172/how-to-design-an-immutable-object-with-complex-initialization?rq=1 – aquinas

+0

Bạn đang khởi tạo các thuộc tính này ngay bây giờ như thế nào? –

Trả lời

13

Điều này có nghĩa là hàm tạo trên lớp này thực sự cần có 13 tham số trong thiết kế không thay đổi?

Nói chung, có. Một loại bất biến với 13 thuộc tính sẽ yêu cầu một số phương tiện để khởi tạo tất cả các giá trị đó.

Nếu chúng không được sử dụng hoặc nếu một số thuộc tính có thể được xác định dựa trên các thuộc tính khác, thì bạn có thể có một hoặc nhiều nhà xây dựng quá tải với ít thông số hơn. Tuy nhiên, một hàm tạo (có hoặc không kiểu là không thay đổi) thực sự nên hoàn toàn khởi tạo dữ liệu cho loại theo cách mà kiểu đó là "chính xác" và "hoàn chỉnh" một cách hợp lý.

Lớp này đại diện cho một số định dạng trên đĩa thực sự có nhiều thuộc tính này, do đó, tái cấu trúc nó thành các bit nhỏ hơn khá nhiều trong câu hỏi tại thời điểm này.

Nếu định dạng "trên đĩa" là thứ được xác định trong thời gian chạy, bạn có thể có phương pháp hoặc nhà xây dựng nhà máy lấy dữ liệu khởi tạo (ví dụ: tên tệp? V.v) và xây dựng hoàn toàn khởi tạo loại cho bạn.

2

Bạn có thể tạo cấu trúc, nhưng sau đó bạn vẫn phải khai báo cấu trúc. Nhưng luôn luôn có mảng và như vậy. Nếu chúng là tất cả các kiểu dữ liệu giống nhau, bạn có thể nhóm chúng theo nhiều cách, chẳng hạn như mảng, danh sách hoặc chuỗi. Có vẻ như bạn đã đúng, tất cả các loại bất biến của bạn phải trải qua hàm tạo theo một cách nào đó, thời tiết thông qua 13 tham số, hoặc thông qua một cấu trúc, mảng, danh sách, v.v ...

+0

Ồ, tôi mới nhận ra rằng hầu hết trong số đó là các chuỗi, vì vậy tôi đã đúng khi nói để sử dụng một mảng. –

+2

Một mảng thực sự không hoạt động vì nó phá hủy ý nghĩa ngữ nghĩa của các tham số trong cấu trúc. Một người nào đó sử dụng lớp học sẽ phải nhớ rằng phần tử thứ 5 trong mảng có nghĩa là "Chi tiết" hoặc một thứ gì đó như thế tồi tệ hơn là chỉ rời khỏi lớp có thể thay đổi. –

+0

Phải, thì cấu trúc là lựa chọn thực sự duy nhất của bạn. Thêm vào đó nếu tôi đúng, trong C#, một cấu trúc có thể sử dụng các giá trị mặc định, có thể hữu ích trong tình huống của bạn. –

3

Có thể giữ cho lớp hiện tại của bạn như vậy, cung cấp các mặc định hợp lý nếu có thể và đổi tên thành IssueRecordOptions. Sử dụng điều này như một tham số khởi tạo duy nhất cho IssueRecord không thay đổi của bạn.

+0

Đây là một lựa chọn tốt nếu yêu cầu bất biến chỉ cần thiết cho một phần của hệ thống. Tuy nhiên, bạn vẫn đang xử lý các loại có thể thay đổi, vì bạn phải thay đổi loại "Tùy chọn" ban đầu. –

3

Bạn có thể sử dụng kết hợp các đối số được đặt tên và tùy chọn trong hàm tạo của mình. Nếu các giá trị luôn luôn khác nhau, thì có, bạn đang mắc kẹt với một nhà xây dựng điên.

14

Để giảm số lượng đối số bạn có thể nhóm chúng thành các bộ hợp lý, nhưng để có đối tượng thật bất biến, bạn phải khởi tạo nó trong phương thức khởi tạo/nhà máy.

Một số biến thể là tạo lớp "trình tạo" mà bạn có thể định cấu hình với giao diện thông thạo và yêu cầu đối tượng cuối cùng. Điều này sẽ có ý nghĩa nếu bạn thực sự có kế hoạch để tạo ra nhiều đối tượng như vậy ở những nơi khác nhau của mã, nếu không nhiều đối số ở một nơi duy nhất có thể chấp nhận được sự cân bằng.

var immutable = new MyImmutableObjectBuilder() 
    .SetProp1(1) 
    .SetProp2(2) 
    .Build(); 
0

Nếu mục đích của bạn là cấm phân công trong thời gian biên soạn, hơn bạn phải tuân theo nhiệm vụ của người xây dựng và người định cư riêng. Tuy nhiên nó có rất nhiều nhược điểm - bạn không thể sử dụng khởi tạo mới thành viên, cũng không phải xml deseralization và vv

tôi sẽ đề nghị một cái gì đó như thế này:

public class IssuerRecord 
    { 
     public string PropA { get; set; } 
     public IList<IssuerRecord> Subrecords { get; set; } 
    } 

    public class ImmutableIssuerRecord 
    { 
     public ImmutableIssuerRecord(IssuerRecord record) 
     { 
      PropA = record.PropA; 
      Subrecords = record.Subrecords.Select(r => new ImmutableIssuerRecord(r)); 
     } 

     public string PropA { get; private set; } 
     // lacks Count and this[int] but it's IReadOnlyList<T> is coming in 4.5. 
     public IEnumerable<ImmutableIssuerRecord> Subrecords { get; private set; } 

     // you may want to get a mutable copy again at some point. 
     public IssuerRecord GetMutableCopy() 
     { 
      var copy = new IssuerRecord 
          { 
           PropA = PropA, 
           Subrecords = new List<IssuerRecord>(Subrecords.Select(r => r.GetMutableCopy())) 
          }; 
      return copy; 
     } 
    } 

IssuerRecord ở đây là nhiều hơn nữa mô tả và hữu ích. Khi bạn vượt qua nó ở một nơi khác, bạn có thể dễ dàng tạo ra phiên bản bất biến. Mã hoạt động trên bất biến nên có logic chỉ đọc, vì vậy nó không thực sự quan tâm nếu nó là cùng loại với IssuerRecord. Tôi tạo ra một bản sao của mỗi lĩnh vực thay vì chỉ gói các đối tượng bởi vì nó vẫn có thể được thay đổi ở một nơi khác, nhưng nó có thể không cần thiết đặc biệt là cho các cuộc gọi đồng bộ tuần tự. Tuy nhiên, an toàn hơn là lưu toàn bộ bản sao bất biến vào một số bộ sưu tập "cho sau này". Nó có thể là một trình bao bọc cho các ứng dụng khi bạn muốn một số mã cấm sửa đổi nhưng vẫn có khả năng nhận các cập nhật cho trạng thái đối tượng.

var record = new IssuerRecord { PropA = "aa" }; 
if(!Verify(new ImmutableIssuerRecord(record))) return false; 

nếu bạn nghĩ theo thuật ngữ C++, bạn có thể thấy ImmutableIssuerRecords là "IssuerRecord const". Bạn phải sử dụng extracare để bảo vệ các đối tượng được sở hữu bởi đối tượng bất biến của bạn, đó là lý do tại sao tôi đề nghị tạo một bản sao cho tất cả trẻ em (ví dụ Subrecords).

ImmutableIssuerRecord.Subrecors là IEnumerable ở đây và thiếu Count và [], nhưng IReadOnlyList đang đến 4.5 và bạn có thể sao chép từ tài liệu nếu muốn (và dễ dàng di chuyển sau này).

có cách tiếp cận khác là tốt, chẳng hạn như Freezable:

public class IssuerRecord 
{ 
    private bool isFrozen = false; 

    private string propA; 
    public string PropA 
    { 
     get { return propA; } 
     set 
     { 
      if(isFrozen) throw new NotSupportedOperationException(); 
      propA = value; 
     } 
    } 

    public void Freeze() { isFrozen = true; } 
} 

mà làm cho mã dễ đọc hơn một lần nữa, và không cung cấp bảo vệ thời gian biên dịch. nhưng bạn có thể tạo các đối tượng như bình thường và sau đó đóng băng chúng sau khi chúng đã sẵn sàng.

Mẫu trình tạo cũng là điều cần xem xét nhưng nó thêm quá nhiều mã "dịch vụ" từ quan điểm của tôi.

+0

Bạn có thể sử dụng deserialization XML tốt. Bạn chỉ cần sử dụng DataContractSerializer thay vì XmlSerializer. Đôi vơi tôi vậy la tôt rôi. –

+0

Vâng, đây là đề nghị thứ hai của tôi, nó chỉ là điều này làm cho ứng dụng của bạn trông giống như một máy Rube Goldberg. –

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