Có phải thực tế xấu khi chỉ sử dụng một giao diện để phân loại?


12

Ví dụ:

Nói rằng tôi có các lớp học A, B, C. Tôi có hai giao diện, hãy gọi chúng IAnimalIDog. IDogkế thừa từ IAnimal. ABIDogs, trong khi Ckhông, nhưng nó là một IAnimal.

Phần quan trọng là IDogcung cấp không có chức năng bổ sung. Nó chỉ được sử dụng để cho phép AB, nhưng không C, được truyền dưới dạng đối số cho các phương thức nhất định.

Đây có phải là thực hành xấu?



5
@JarrodRoberson trong khi tôi có xu hướng đồng ý, nếu bạn làm việc trong thế giới .Net, bạn sẽ bị mắc kẹt với nó (hoặc sẽ đi ngược lại quy ước đã được thiết lập nếu bạn làm khác đi).
Daniel B

2
@JarrodRoberson IMHO MyProject.Data.IAnimalMyProject.Data.Animaltốt hơn MyProject.Data.Interfaces.AnimalMyProject.Data.Implementations.Animal
Mike Koder

1
Vấn đề là không nên có bất kỳ lý do nào để quan tâm nếu đó là một Interfacehoặc Implementation, dù là trong một tiền tố lặp đi lặp lại hoặc một không gian tên, thì đó cũng là một tautology và vi phạm DRY. Doglà tất cả những gì bạn nên quan tâm. PitBull extends Dogcũng không cần dự phòng triển khai, từ này extendscho tôi biết tất cả những gì tôi cần biết, hãy đọc liên kết tôi đã đăng trong nhận xét ban đầu của mình.

1
@Jarrod: Đừng phàn nàn với tôi. Khiếu nại với nhóm phát triển .NET. Nếu tôi đi ngược lại tiêu chuẩn, các đồng nghiệp của tôi sẽ ghét lòng can đảm của tôi.
Kendall Frey

Câu trả lời:


13

Giao diện không có bất kỳ thành viên nào hoặc có chính xác các thành viên với giao diện khác được kế thừa từ cùng một giao diện có tên là Giao diện đánh dấu và bạn đang sử dụng nó làm điểm đánh dấu.

Nó không phải là thực tiễn xấu nhưng giao diện có thể được thay thế bằng các thuộc tính (chú thích) nếu ngôn ngữ mà bạn đang sử dụng hỗ trợ nó.

Tôi có thể tự tin nói rằng đó không phải là thực tiễn tồi vì tôi đã thấy mô hình "Giao diện đánh dấu" sử dụng nhiều trong Dự án Orchard

Dưới đây là mẫu từ dự án Orchard.

public interface ISingletonDependency : IDependency {}

/// <summary>
/// Base interface for services that may *only* be instantiated in a unit of work.
/// This interface is used to guarantee they are not accidentally referenced by a singleton dependency.
/// </summary>
public interface IUnitOfWorkDependency : IDependency {}

/// <summary>
/// Base interface for services that are instantiated per usage.
/// </summary>
public interface ITransientDependency : IDependency {}

Vui lòng tham khảo Giao diện đánh dấu .


Các thuộc tính / chú thích có hỗ trợ kiểm tra thời gian biên dịch (sử dụng C # làm ngôn ngữ tham chiếu) không? Tôi biết tôi có thể ném ngoại lệ nếu đối tượng không có thuộc tính, nhưng mẫu giao diện bắt lỗi khi biên dịch.
Kendall Frey

Nếu bạn đang sử dụng Giao diện đánh dấu để bạn chưa có lợi ích kiểm tra biên dịch. Bạn đang cơ bản làm loại kiểm tra theo giao diện.
Freshblood

Tôi bối rối. Mẫu 'Giao diện đánh dấu' này cung cấp kiểm tra thời gian biên dịch, theo đó bạn không thể truyền Cmột phương thức mong đợi một phương thức IDog.
Kendall Frey

Nếu bạn đang sử dụng mô hình này để bạn đang thực hiện các công việc phản chiếu. Tôi có nghĩa là bạn năng động hoặc thống kê làm kiểm tra loại. Tôi chắc chắn rằng bạn có điều gì đó không đúng trong trường hợp sử dụng của bạn nhưng tôi chưa thấy bạn đang sử dụng nó như thế nào.
Freshblood

Hãy để tôi mô tả nó như tôi đã làm trong nhận xét về câu trả lời của Telastyn. ví dụ: tôi có một Kennellớp lưu trữ một danh sách IDogs.
Kendall Frey

4

Vâng, đó là một thực hành xấu trong hầu hết các ngôn ngữ.

Các loại không có để mã hóa dữ liệu; chúng là một trợ giúp để chứng minh rằng dữ liệu của bạn đi đến nơi cần đến và hành xử như nó cần phải hành xử. Lý do duy nhất để loại phụ là nếu một hành vi đa hình thay đổi (và không phải lúc nào cũng vậy).

Vì không có gõ, IDog có thể làm gì được ngụ ý. Một lập trình viên nghĩ một điều, một điều khác nghĩ khác và mọi thứ đổ vỡ. Hoặc những thứ nó thể hiện tăng lên khi bạn cần kiểm soát chi tiết hơn.

[sửa: làm rõ]

Ý tôi là bạn đang thực hiện một số logic dựa trên giao diện. Tại sao một số phương pháp chỉ có thể làm việc với chó và những người khác bất kỳ động vật? Sự khác biệt đó là một hợp đồng ngụ ý vì không có thành viên thực sự cung cấp sự khác biệt; không có dữ liệu thực tế trong hệ thống. Sự khác biệt duy nhất là giao diện (do đó bình luận 'không gõ') và điều đó có nghĩa là gì sẽ khác nhau giữa các lập trình viên. [/biên tập]

Một số ngôn ngữ cung cấp các cơ chế đặc điểm trong đó điều này không quá tệ nhưng những ngôn ngữ này có xu hướng tập trung vào các khả năng, không phải là sàng lọc. Tôi có ít kinh nghiệm với họ vì vậy không thể nói với các thực tiễn tốt nhất ở đó. Trong C ++ / Java / C # mặc dù ... không tốt.


Tôi bối rối bởi hai đoạn giữa của bạn. "Loại phụ" nghĩa là gì? Tôi đang sử dụng các giao diện, là hợp đồng, không phải là triển khai và tôi không chắc cách áp dụng ý kiến ​​của bạn. Ý bạn là gì khi 'không gõ'? Bạn có ý nghĩa gì bởi 'ngụ ý'? IDog có thể làm mọi thứ mà IAnimal có thể, nhưng không được đảm bảo để có thể làm được nhiều hơn.
Kendall Frey

Cảm ơn bạn đã làm rõ. Trong trường hợp cụ thể của tôi, tôi không chính xác sử dụng các giao diện khác nhau. Nó có thể được mô tả tốt nhất bằng cách nói rằng tôi có một Kennellớp lưu trữ một danh sách các IDogs.
Kendall Frey

@KendallFrey: Hoặc là bạn đang bỏ qua giao diện (xấu) hoặc bạn đang tạo ra sự khác biệt giả tạo có mùi trừu tượng không chính xác.
Telastyn

2
@Telastyn - Mục đích của giao diện là cung cấp siêu dữ liệu
Freshblood

1
@Telastyn: Vậy làm thế nào để các thuộc tính áp dụng cho kiểm tra thời gian biên dịch? AFAIK, trong C #, các thuộc tính chỉ có thể được sử dụng khi chạy.
Kendall Frey

2

Tôi chỉ biết rõ về C #, vì vậy tôi không thể nói điều này cho lập trình OO nói chung, nhưng như một ví dụ về C #, nếu bạn cần phân loại các lớp, các tùy chọn rõ ràng là giao diện, thuộc tính enum hoặc thuộc tính tùy chỉnh. Thông thường tôi sẽ chọn giao diện bất kể người khác nghĩ gì.

if(animal is IDog)
{
}
if(animal.Type == AnimalType.Dog)
{
}
if(animal.GetType().GetCustomAttributes(typeof(DogAttribute), false).Any())
{
}


var dogs1 = animals.OfType<IDog>();
var dogs2 = animals.Where(a => a.Type == AnimalType.Dog);
var dogs3 = animals.Where(a => a.GetType().GetCustomAttributes(typeof(DogAttribute), false).Any());


var dogsFromDb1 = session.Query<IDog>();
var dogsFromDb2 = session.Query<Animal>().Where(a => a.Type == AnimalType.Dog);
var dogsFromDb3 = session.Query<Animal>().Where(a => a.GetType().GetCustomAttributes(typeof(DogAttribute),false).Any());


public void DoSomething(IDog dog)
{
}
public void DoSomething(Animal animal)
{
    if(animal.Type != AnimalType.Dog)
    {
        throw new ArgumentException("This method should be used for dogs only.");
    }
}
public void DoSomething(Animal animal)
{
    if(!animal.GetType().GetCustomAttributes(typeof(DogAttribute), false).Any())
    {
        throw new ArgumentException("This method should be used for dogs only.");
    }
}

Phần cuối cùng của mã chủ yếu là những gì tôi sử dụng này cho. Tôi có thể thấy rằng phiên bản giao diện rõ ràng là ngắn hơn, cũng như được kiểm tra thời gian biên dịch.
Kendall Frey

Tôi có trường hợp sử dụng simillar nhưng hầu hết các nhà phát triển .net khuyên mạnh mẽ chống lại nó, nhưng tôi sẽ không sử dụng các thuộc tính
GorillaApe

-1

Không có gì sai khi có một giao diện kế thừa giao diện khác mà không cần thêm thành viên mới, nếu tất cả các triển khai của giao diện sau sẽ có thể thực hiện một cách hữu ích những điều mà một số triển khai trước đây không thể thực hiện được. Thật vậy, đó là một mẫu tốt mà IMHO nên được sử dụng nhiều hơn trong những thứ như .net Framework, đặc biệt là khi người triển khai có lớp sẽ tự nhiên thỏa mãn các ràng buộc được ngụ ý bởi giao diện có nguồn gốc nhiều hơn có thể chỉ ra rằng chỉ bằng cách thực hiện giao diện có nguồn gốc nhiều hơn , mà không phải thay đổi bất kỳ mã khai báo thành viên hoặc khai báo nào.

Ví dụ: giả sử tôi có các giao diện:

giao diện IMaybeMutableFoo {
  Bar Thing {get; set;} // Và các thuộc tính khác nữa
  bool IsMutable;
  IImmutableFoo AsImmutable ();
}
giao diện IImmutableFoo: IMaybeMutableFoo {};

Một triển khai IImmutableFoo.AsImmutable()sẽ được dự kiến ​​sẽ trở lại chính nó; một lớp triển khai có thể thay đổi IMaybeMutableFoo.AsImmutable()sẽ được mong đợi sẽ trả về một đối tượng mới không thay đổi sâu có chứa ảnh chụp nhanh của dữ liệu. Một thói quen muốn lưu trữ một ảnh chụp nhanh của dữ liệu được giữ trong một IMaybeMutableFoocó thể cung cấp quá tải:

khoảng trống công khai StoreData (IImmutableFoo ThingToStore)
{
  // Lưu trữ tài liệu tham khảo sẽ đủ, vì ThingToStore được biết là không thay đổi
}
khoảng trống công khai StoreData (IMaybeMutableFoo ThingToStore)
{
  StoreData (ThingToStore.AsImmutable ()); // Gọi quá tải cụ thể hơn
}

Trong trường hợp trình biên dịch không biết rằng thứ được lưu trữ là một IImmutableFoo, nó sẽ gọi AsImmutable(). Hàm sẽ trả về nhanh nếu mục đã không thay đổi, nhưng một lệnh gọi qua giao diện vẫn mất thời gian. Nếu trình biên dịch biết rằng mục đó đã là một IImmutableFoo, nó có thể bỏ qua lệnh gọi hàm không cần thiết.


Downvoter: Quan tâm để bình luận? Đôi khi thừa hưởng một giao diện mà không thêm bất cứ điều gì mới là điều ngớ ngẩn, nhưng điều đó không có nghĩa là sẽ không có lúc đó là một kỹ thuật tốt (và IMHO không được sử dụng).
supercat
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.