Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/oop/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Oop 存储库模式:泛型与多态实现方式_Oop_Generics_Inheritance_Polymorphism_Repository Pattern - Fatal编程技术网

Oop 存储库模式:泛型与多态实现方式

Oop 存储库模式:泛型与多态实现方式,oop,generics,inheritance,polymorphism,repository-pattern,Oop,Generics,Inheritance,Polymorphism,Repository Pattern,通用存储库界面如下所示: public interface IRepository<T> where T : Entity { T Get(int key); IQueryable<T> Get(); void Save(T obj); void Delete(T obj); } 公共接口i假设,其中T:Entity { T Get(int键); IQueryable Get(); 无效保存(T obj); 无效删除(T obj)

通用存储库界面如下所示:

 public interface IRepository<T> where T : Entity
{
    T Get(int key);

    IQueryable<T> Get();

    void Save(T obj);

    void Delete(T obj);
}
公共接口i假设,其中T:Entity
{
T Get(int键);
IQueryable Get();
无效保存(T obj);
无效删除(T obj);
}
实现类似功能的另一种方法是使用多态性,如下所示

public interface IRepository
{
    Entity Get(int key);

    IQueryable<Entity> Get();

    void Save(Entity obj);

    void Delete(Entity obj);
}
公共接口IRepository
{
实体Get(int键);
IQueryable Get();
作废保存(实体obj);
作废删除(实体obj);
}

一般来说,我的问题是我们应该在哪些场景或用例中使用泛型?如果我们使用多态性,是否可以完全避免泛型。或者我在这里完全没有意义,这两个完全无关

您的第一个示例和第二个示例之间最关键的区别称为

我假设您是从静态类型语言(如C#或Java)的角度提出这个问题的

在使用泛型的版本中,编译器确保您始终使用正确的类型,而在第二个版本(使用更通用类型的版本)中,您需要自己手动检查。此外,编译器将不断强制您将常规类型(例如
实体
)转换为更具体的类型(例如
客户
),以使用对象提供的服务

换句话说,您必须始终与编译器抗争,因为它始终要求我们强制转换类型,以便它能够验证我们编写的代码

使用泛型

第一个示例在接口级别使用类型变量
T
。这意味着,当您为该接口类型定义变量时(或当您实现它时),您也将被迫为
T
提供类型参数

比如说

IRepository<Customer> custRepo = ...;
因此,当您使用它时,编译器会强制您遵守类型参数:

Customer customer = custRepo.Get(10);
customer.setEmail("lskywalker@gmail.com");
custRepo.Save(customer);
在上面的示例中,所有存储库方法仅适用于
Customer
类型。因此,我不能将不正确的类型(例如Employee)传递给方法,因为编译器将在任何地方强制执行类型安全:

Employee employee = empRepo.Get(10);
custRepo.Save(employee); //Uh oh! Compiler error here
没有泛型

另一方面,在第二个示例中,您所做的只是决定使用更通用的类型。通过这样做,您可以牺牲类型安全性来换取一些灵活性:

例如:

IRepository custRepo = ...;
Entity customer = custRepo.Get(10); 
((Customer) customer).setEmail("lskywalker@gmail.com"); //Now you'll need casting
custRepo.Save(customer);
在上述情况下,您必须始终将
实体
转换为更可用的类型,如
客户
,以便能够使用它提供的内容。这种强制转换是一种不安全的操作,它可能会引入bug(如果我们在对所使用类型的假设中犯了错误)

此外,存储库类型不会阻止您将错误的类型传递给方法,并且您可能会犯语义错误:

Entity employee = empRepo.Get(10);
custRepo.Save(employee); //Uh Oh! 
如果这样做,您可能必须在
CustomerRepo
中确保您的实体实际上是
客户的实体,以防止出现类似上面示例最后一行中的错误

换句话说,您将手动实现编译器在使用泛型时自动提供的类型安全性

这听起来好像我们正在尝试使用静态类型的语言,好像它是一种动态类型的语言,对吗?这就是为什么我们必须一路与编译器抗争来实施这种编程风格

关于参数多态性

现在,您可能想探索泛型也是多态性的一种形式的想法。您可能还想阅读我在另一个问题中给出的这篇文章,其中我引用了一篇关于多态性的优秀论文,这篇论文可能会帮助您扩展对该主题的理解,而不仅仅是类和接口继承

动态类型语言与静态类型语言之争

现在,一个有趣的结论可能会帮助您进一步探索这一点,即动态语言(如JavaScript、Python、Ruby等),您不必进行显式类型声明,实际上可以像您的泛型免费示例一样工作

动态语言的工作方式就好像所有变量都是
Object
类型,它们只允许您对该对象执行任何操作,以避免您必须不断地将对象强制转换为不同的类型。程序员有责任编写测试,以确保始终正确使用对象

静态类型语言的捍卫者和喜欢动态类型语言的捍卫者之间一直存在着激烈的争论。这就是我们通常所说的“a”

我相信,这实际上是一个您可能希望更深入地探索的主题,以真正理解两个示例之间的根本区别,并了解泛型的静态类型和类型安全性与仅使用动态类型的高度灵活性相比如何

我建议你读一篇优秀的论文,作者是伯恩茅斯大学的劳伦斯·特拉特

现在,在C#或Java等静态类型语言中,我们通常希望尽可能地利用类型安全性。但是没有什么能阻止我们像在动态语言中那样编写代码,只是编译器会不断地与我们抗争。这就是你的第二个例子


如果这是一种与你和你的工作风格产生共鸣的编程方式,或者如果它提供了你所寻求的灵活性,那么也许你应该考虑使用动态类型语言。也许可以在您当前的平台上进行组合(例如,或),这样您也可以重用当前静态类型语言中已有的代码。

反问:为什么要避免泛型?@DevSolar这不是关于避免泛型。这是关于理解使用
Entity employee = empRepo.Get(10);
custRepo.Save(employee); //Uh Oh!