5

Đây sẽ là một câu hỏi hơi trừu tượng.Làm cách nào để tránh trùng lặp mã khi lập mô hình bảng, bố cục và bản ghi của bảng, tất cả đều chia sẻ cấu trúc cơ bản giống nhau?

Tôi đang làm việc trên khuôn khổ Lớp truy cập dữ liệu cần phân biệt giữa bảng, lược đồ/bố cục trừu tượng và bản ghi bảng bê tông. Tôi sợ rằng vì sự khác biệt này, sẽ có nhiều sự sao chép mã. Tôi có thể cần một số đầu vào về cách để tránh điều này.

+-----------+ 
| Foo | 
+-----------+ 
| +Id: Guid | 
+-----------+ 

Lưu ý rằng sơ đồ này có thể mô tả bất kỳ trong số này: a schema bảng, một bảng cụ thể, hoặc một kỷ lục bảng bê tông, có một lĩnh vực Id với loại Guid.

  • Tất cả những gì được biết trong lược đồ là tên và loại của trường.
  • Trong bảng bê tông (mở), "chỉ mục cột" của trường được biết thêm.
  • Với hồ sơ, tất cả những điều này được biết, cộng với trường có giá trị cụ thể.

Dịch mã này thành mã, tôi sẽ nhận được nhiều loại tương tự (theo cặp ba). Tôi sẽ sử dụng các giao diện để giữ cho ví dụ ngắn gọn; những gì tôi muốn thể hiện là sự giống nhau của các loại:

// these interfaces only need to be implemented once: 

interface ISchemaField<T> { string Name { get; }  } 


interface ITableField<T> { string Name { get; }   
           int Index { get; }  } 

interface IRecordField<T> { string Name { get; }   
           int Index { get; }  
           T  Value { get; set; } } 

// these three interfaces are an example for one entity; there would be 
// three additional types for each additional entity. 

interface IFooSchema 
{ 
    ISchemaField<Guid> Id { get; } 
    IFooTable Open(IDbConnection dbConnection, string tableName); 
} 

interface IFooTable 
{ 
    ITableField<Guid> Id { get; } 
    ICollection<IFooRecord> ExecuteSomeQuery(); 
} 

interface IFooRecord 
{ 
    IRecordField<Guid> Id { get; } 
} 

Bây giờ tôi muốn tránh phải viết ba triển khai rất giống nhau cho tất cả các thực thể trong một mô hình dữ liệu cụ thể. Một số cách có thể để giảm trùng lặp mã là gì?

  • Tôi đã nghĩ đến việc sinh mã (ví dụ T4), đó sẽ là một giải pháp OK, nhưng tôi muốn một giải pháp "thủ công" mã hóa (nếu có) với ít dòng mã & dễ đọc hơn mã.

  • Tôi đã nghĩ về việc tạo một lớp cho mỗi thực thể thực hiện lược đồ, bảng và giao diện bản ghi cùng một lúc ... nhưng điều đó cảm thấy lộn xộn và giống như vi phạm Tách mối quan tâm.

+0

Bạn có đang sử dụng Khung thực thể không? – TheBoyan

+0

@Bojan, no. Tôi đang xây dựng một khung tùy chỉnh DAL trên đầu trang của một API (ESRI ArcObjects) mà không được hỗ trợ bởi Entity Framework, cũng như NHibernate, cũng như bất kỳ OR/M khác mà tôi biết. – stakx

+0

Chỉ cần một cái nhìn ngắn gọn về lược đồ của bạn làm cho tôi tự hỏi tại sao không có kế thừa giữa ba giao diện lược đồ? Bạn dường như chỉ sao chép siêu dữ liệu thay vì kế thừa chúng. –

Trả lời

1

IMHO, tôi nghĩ bạn đang đi quá xa với sự trừu tượng của cấu trúc bảng. Việc sử dụng quá nhiều giao diện có thể làm cho mã khó đọc. Tôi thấy nó rất khó chịu, ví dụ, rằng bạn không thể nhấn F12 để xem việc thực hiện một đối tượng bởi vì nó là một loại giao diện.

Một mô hình đã được chứng minh mà tôi đã làm việc trong nhiều năm và siêu dễ bảo trì là giữ cho tất cả các tên lớp giống hệt với tên bảng, sau đó tên trường khớp với tên cột.Sau đó bạn có thể dễ dàng tạo ra các phương thức chỉ bằng cách sử dụng tìm kiếm và thay thế trong trình soạn thảo, và tương tự với các thay đổi mã. Bằng cách này bạn không cần phải giữ tên cột và chỉ mục cột trong bộ nhớ. Bạn chỉ cần hardcode chúng trong tầng truy cập dữ liệu của bạn (HARDCODING KHÔNG LUÔN BAD!). Ví dụ:

this.Price = reader["Price"] as decimal?; 

Hiệu suất là rất tốt với cách tiếp cận và mã này là siêu duy trì!

Bất kể phương pháp nào bạn làm theo, điều quan trọng là cách bạn giữ ánh xạ từ cột bảng thành thuộc tính lớp. Tôi khuyên bạn nên mã hóa chúng và sử dụng quy ước đặt tên đơn giản (tên cột = tên thuộc tính). Cách tiếp cận này yêu cầu bạn biên dịch lại khi bạn thêm hoặc thay đổi cột, nhưng tôi nghĩ những thay đổi này không xảy ra thường xuyên đủ để biện minh cho việc có tên cột trong biến hoặc đọc chúng từ một tệp riêng biệt. Làm như vậy tránh biên dịch lại, nhưng làm cho mã ít dễ làm theo. Tôi không nghĩ nó xứng đáng.

+0

Tôi đã sử dụng các giao diện trong ví dụ của mình vì tôi cảm thấy rằng việc triển khai cụ thể sẽ chỉ phân tâm từ vấn đề thực sự.Giao diện là đủ để hiển thị nơi trùng lặp mã sẽ xảy ra. Nhưng tôi có thể sử dụng 'class' thay vì' interface'. (Tôi đã cập nhật câu hỏi của mình để làm cho điều này rõ ràng hơn.) – stakx

+0

Tôi không đồng ý với tất cả những gì bạn đang nói, đặc biệt. hardcoding cột chỉ số bit (có vẻ như một nguồn tiềm năng của các lỗi), nhưng tôi chấp nhận câu trả lời của bạn bởi vì câu lệnh của bạn, _ "bạn đang đi một chút quá xa với trừu tượng của cấu trúc bảng" _ bây giờ có vẻ rất hợp lý với tôi. Tôi đã đăng một câu trả lời riêng với những gì tôi đã quyết định làm (xem bên dưới). – stakx

+1

Các thanh danh để tự thiết kế tất cả điều này và không tuân theo mẫu được Microsoft hoặc bất kỳ ai đề xuất. Tôi đã cập nhật câu trả lời của mình để cố gắng biện minh cho mẫu cứng của tôi. – Diego

1

Hãy xem AutoMapper. Đó là một thư viện nhỏ hữu ích để chuyển đổi DTO thành POCO's etc và sẽ loại bỏ một lượng lớn mã trùng lặp và nỗ lực chuyển dữ liệu giữa các lớp trong ứng dụng của bạn.

+0

API cơ sở dữ liệu bên dưới (ESRI ArcObjects) có một số khác biệt khái niệm đối với RDBMS thông thường của bạn. (Đối với một điều, đó là dựa trên COM. Đối với một, hàng trả về bởi một con trỏ DB có thể "hết hạn" ngay sau khi hàng tiếp theo được trả về. Cuối cùng, con trỏ phải được giải phóng một cách rõ ràng. Danh sách đi và về.) - Tôi khá chắc chắn rằng AutoMapper, cũng không phải là các khung công tác OR/M phức tạp hơn, sẽ gặp khó khăn trong việc xử lý nó một cách chính xác. Nhưng tôi rất muốn được chứng minh là sai. – stakx

1

Tạo một hệ thống phân cấp thừa kế giao diện

interface ISchemaField<T> 
{ 
    string Name { get; } 
} 

interface ITableField<T> : ISchemaField<T> 
{ 
    int Index { get; } 
} 

interface IRecordField<T> : ITableField<T> 
{ 
    T Value { get; set; } 
} 

Lớp học thực hiện các giao diện sau đó có thể làm theo các sơ đồ thừa kế tương tự.

+0

Công việc thực sự bắt đầu khi bạn phải * triển khai * những điều này. Và thậm chí tệ hơn các kiểu khung '… Field ' (cần được thực hiện * chỉ một lần *) là các loại thực thể ba (cần phải được thực hiện * cho mỗi thực thể *), trong đó 'IFoo… ' chỉ là một thí dụ. – stakx

0

Tôi đã chấp nhận câu trả lời khác, nhưng nghĩ rằng tôi muốn chia sẻ những gì tôi đã cuối cùng đã quyết định làm:

  • tôi loại bỏ một số trùng lặp bằng cách sáp nhập các lược đồ bảng và bảng vào một loại . Điều này trở thành một loại trạng thái: Một bảng có thể là "không mở" hoặc "mở". Nếu nó không được mở, thì chỉ có tên và loại của trường mới có sẵn. Nếu bảng đã được mở, thì các chỉ mục trường cũng có sẵn.

  • Tên trường là trường tĩnh hoặc chỉ chuỗi hằng số, trong loại bảng.

  • Sau đó tôi kết thúc với một số sao chép trái, cụ thể là sự giống nhau của hai loại, một cho một bản ghi, loại kia cho bảng/lược đồ, nhưng điều này là chấp nhận được.

Tóm lại, tôi đã chọn giải pháp thực dụng hơn tôi muốn; nó không thuần khiết, nhưng có thể sẽ làm mọi thứ.


class FooRecord 
{ 
    FooRecord(FooTable table) { … } 
    readonly FooTable table; 

    public Guid Id { get { … } set { … } } 
} 

class TableField<T> 
{ 
    public TableField(string name) { … } 
    public string Name { get; } 
    public int Index { get; internal set; } 
} 

class FooTable 
{ 
    public void Open(IDbConnection dbConnection, string tableName) { … } 
    //^opens the table and determines the fields' indices, e.g. Id.Index. 

    public TableField<Guid> Id 
    { 
     get 
     { 
      return idField; 
     } 
    } 
    TableField<Guid> idField = new TableField<Guid>("ID"); 
}