2012-05-08 27 views
7

Tôi đang viết một đối tượng phải luôn có giá trị nhất định. Đáng chú ý nhất, nó phải luôn luôn có một giá trị cho tài sản Name.Làm cách nào tôi có thể hủy khởi tạo đối tượng?

public class User 
{ 
    public string Name { get; set; } 

    public User(string name) 
    { 
     Name = name; 
    } 
} 

Bây giờ, có một vài quy tắc kinh doanh mà tôi cần triển khai trong lớp học này. Một trong số đó là thuộc tính Name phải là một tên duy nhất. Vì vậy, tôi sẽ nghĩ rằng initializer cho đối tượng này sẽ trông giống như thế này:

public User(string name, IQueryable<User> allUsers) 
    { 
     var matches = allUsers.Where(q => q.Name == name).ToList(); 
     if(matches.Any()) 
     { 
      // abort object initialization 
     } 
     Name = name; 
    } 

Nhưng tôi không chắc chắn làm thế nào tôi sẽ hủy bỏ việc khởi tạo đối tượng. Trong thực tế, là thậm chí có thể?

Có cách nào để hủy bỏ việc khởi tạo đối tượng (ví dụ: đặt đối tượng thành không) hoặc có cách nào tốt hơn để hoàn thành việc này không?

+0

một lực lượng cách vũ phu, đặt tất cả các lĩnh vực để null, thêm các đối tượng với các lĩnh vực null, loại bỏ các đối tượng – RhysW

+2

này có thể chỉ là mẫu mã nhưng trong trường hợp nó không phải là: Bạn có thể cung cấp một vị cho 'Bất kỳ ', vì vậy bạn không phải đi qua' Where'. –

+0

@BrianRasmussen Cả hai đều có kết quả chính xác, vì vậy nó thực sự sở thích cá nhân mà bạn sử dụng. Ngược lại, lệnh gọi 'ToList' ngăn chặn ngắn mạch của' Any' không đánh giá toàn bộ truy vấn. – Servy

Trả lời

3

Hủy bỏ việc khởi tạo một đối tượng được thực hiện bằng cách ném một ngoại lệ trong hàm tạo và được khuyến nghị từ chối đầu vào không hợp lệ.

public class User 
{ 
    public User(String name) { 
     if (String.IsNullOrWhiteSpace(name)) { 
      if (name == null) { 
       throw new System.ArgumentNullException("Cannot be null.", "name"); 
      } 
      else { 
       throw new System.ArgumentException("Cannot be empty.", "name"); 
      } 
     } 
    } 
} 

Logic nghiệp vụ bạn muốn xác định trong hàm tạo không phù hợp ở đó. Constructors nên nhẹ và chỉ instantiation. Truy vấn một số nguồn dữ liệu quá đắt đối với một hàm tạo. Do đó, bạn nên sử dụng mẫu nhà máy thay thế. Với mô hình nhà máy, người gọi có thể mong đợi có một số việc nâng nặng liên quan đến việc tạo đối tượng.

public class User 
{ 
    private User(String name) { 
     if (String.IsNullOrWhiteSpace(name)) { 
      if (name == null) { 
       throw new System.ArgumentNullException("Cannot be null.", "name"); 
      } 
      else { 
       throw new System.ArgumentException("Cannot be empty.", "name"); 
      } 
     } 
    } 

    public static User CreateUser(String name) { 
     User user = new User(name); // Lightweight instantiation, basic validation 

     var matches = allUsers.Where(q => q.Name == name).ToList(); 

     if(matches.Any())   
     {   
      throw new System.ArgumentException("User with the specified name already exists.", "name");   
     }  

     Name = name; 
    } 

    public String Name { 
     get; 
     private set; // Optionally public if needed 
    } 
} 

Bạn có thể thấy rằng mẫu nhà máy phù hợp hơn và vì đó là phương pháp mà người gọi có thể mong đợi có một số công việc đang diễn ra bằng cách gọi nó. Trong khi đó với một nhà xây dựng một trong những mong đợi nó sẽ được nhẹ.

Nếu bạn muốn đi tuyến đường khởi tạo, khi đó bạn sẽ muốn thử một số phương pháp khác để thực thi các quy tắc kinh doanh của bạn chẳng hạn như khi chèn thực tế vào nguồn dữ liệu.

public class User 
{ 
    public User(String name) { 
     if (String.IsNullOrWhiteSpace(name)) { 
      if (name == null) { 
       throw new System.ArgumentNullException("Cannot be null.", "name"); 
      } 
      else { 
       throw new System.ArgumentException("Cannot be empty.", "name"); 
      } 
     } 
    } 
} 

public class SomeDataSource { 
    public void AddUser(User user) { 
     // Do your business validation here, and either throw or possibly return a value 
     // If business rules pass, then add the user 
     Users.Add(user); 
    } 
} 
+0

Cảm ơn rất nhiều câu trả lời chi tiết như vậy! Tôi đã được thuyết phục để đi tuyến đường Nhà máy mẫu mà bạn vạch ra. – quakkels

+0

Bạn đang rất hoan nghênh, vui vì tôi có thể giúp đỡ. –

4

Tôi cho rằng bạn có thể kiểm tra và ném một ngoại lệ trong hàm tạo của đối tượng hoặc Tên setter, nhưng eeeehhhh có thể đi kèm với một loạt các vấn đề và mối quan tâm hỗn hợp. Tôi nói tạo đối tượng thông qua một nhà máy mà kiểm tra này và trả về null (hoặc một ngoại lệ có tên độc đáo). Hoặc tạo đối tượng POCO và thực hiện xác thực thông qua một lớp/phương thức riêng biệt.

2

Bạn có thể nên kiểm tra tên trùng lặp trước khi tạo Người dùng.

+1

Bạn có thể nhận được một điều kiện chủng tộc. – jason

+0

Bạn luôn có thể có điều kiện chủng tộc :-) – Steven

6

Vâng, bạn chỉ cần ném một ngoại lệ. Nhưng tôi không thích cách xử lý vấn đề này chút nào. Thay vào đó, bạn nên tạo người dùng thông qua dịch vụ và kiểm tra dịch vụ nếu tên đó có hợp lệ hay không.

2

Cá nhân, tôi chạy kiểm tra logic trước khi tôi khởi tạo. Ví dụ:

if(UserLogic.PreInsertValidation(string username)){ 
    User newUser = new User(username); 
} 
else{ 
    // Handling - maybe show message on client "The username is already in use." 
} 

PreInsertValidation sẽ có tất cả kiểm tra logic nghiệp vụ dựa trên yêu cầu của bạn.

0

Điều bạn đang tìm kiếm là mẫu identity map hoặc loại này. Có lẽ là sai khi để lại trách nhiệm này trong chính đối tượng, nó nên được thực hiện trong thành phần tạo ra các thực thể. tất nhiên bản đồ shoul là chủ đề an toàn nếu cần thiết, để tránh điều kiện chủng tộc.

0

Tôi sẽ xử lý việc này trong cam kết có người dùng trong bộ sưu tập. Ví dụ, nếu bạn đang chỉnh sửa một bộ sưu tập người dùng và sau đó liên tục chúng vào cơ sở dữ liệu, lớp kiên trì sẽ chịu trách nhiệm cho việc xác thực. Tôi đã luôn coi việc thực hành không tốt để có một đối tượng chịu trách nhiệm duy trì tất cả các vật thể khác giống như nó. Nó giới thiệu một mối quan hệ cha mẹ con với chính đối tượng khi không có. Tôi đề nghị thực hiện một công cụ xác nhận của một số loại để xử lý này.

3

Thay vì có một constructor công cộng, có một phương pháp như thế này và xây dựng tư nhân:

public static User CreateUser(string name) 
{ 
     // Check whether user already exists, if so, throw exception/return null 

     // If name didn't exist, record that this user name is now taken. 
     // Construct and return the user object 
     return new User(name); 
} 

private User(string name) 
{ 
     this.Name = name; 
} 

Sau đó, mã gọi của bạn có thể sử dụng User myUser = User.CreateUser("Steve"); và xử lý rỗng trở lại/ngoại lệ cho phù hợp.

Điều đáng nói là việc sử dụng bất kỳ phương pháp nào bạn đang sử dụng lưu trữ tên người dùng nào sẽ được cập nhật để nói rằng tên này được thực hiện trong phương thức CreateUser. Nếu không, nếu bạn chờ một lúc trước khi lưu trữ đối tượng này trong cơ sở dữ liệu hoặc một cái gì đó, bạn vẫn sẽ gặp sự cố. Tôi đã cập nhật mã ở trên để làm cho điều này rõ ràng hơn.

+1

Bằng cách này bạn có thể sử dụng một 'IQueryable' tĩnh để giữ tất cả người dùng (hoặc tham chiếu đến một kho lưu trữ của tất cả người dùng) và tránh điều kiện cuộc đua bằng khóa. – SWeko

+0

@SWeko Cả hai phương pháp này và sử dụng một nhà xây dựng có điều kiện chủng tộc nếu không có khóa, và cả hai có thể loại bỏ các điều kiện chủng tộc bằng cách thêm các khóa thích hợp. – Servy

0

Thay vì thực hiện xác thực này bên trong chính đối tượng, hãy đặt việc tạo, xác thực và lưu thực thể này trong một dịch vụ. Dịch vụ này có thể ném ValidationException khi tên người dùng không phải là duy nhất và thậm chí có thể bắt đầu một giao dịch để đảm bảo rằng không có điều kiện chủng tộc nào có thể xảy ra. Một mô hình tốt tôi sử dụng là mẫu command/handler. Dưới đây là ví dụ:

public class CreateNewUserCommand 
{ 
    public string UserName { get; set; } 
} 

internal class CreateNewUserCommandHandler 
    : ICommandHandler<CreateNewUserCommand> 
{ 
    private readonly IUnitOfWork uow; 

    public CreateNewUserCommandHandler(
     IUnitOfWork uow) 
    { 
     this.uow = uow; 
    } 

    public void Handle(CreateNewUserCommand command) 
    { 
     // TODO Validation 

     var user = new User { Name = command.Name }; 

     this.uow.Users.InsertOnSubmit(user); 
    } 
} 

Bạn thậm chí có thể thêm xác thực vào lớp riêng của mình.

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