gpt4 book ai didi

c# - 给定具有相同基类的不同类型对象列表,一个完全独立的类如何在没有 if/else 逻辑的情况下操作每个对象?

转载 作者:太空宇宙 更新时间:2023-11-03 12:14:44 25 4
gpt4 key购买 nike

上下文:以下伪代码示例演示了贯穿我的项目的 [anti-] 模式。此 C# 示例代码很快就打出来了,如有错误请见谅。

挑战:为了追求使用良好的 OOP 实践,例如多态性和单一职责,我试图尽可能地避免分支(if/else,switch/case)。

示例类的设置:

class Person {
public string Name;
}

class Employee : Person {
public int EmployeeID;
}

class Customer : Person {
public int CustomerID;
}

class PersonDao {
public void Save(Person person){
// .. save Person to database
}
}

class EmployeeDao : PersonDao {
public override void Save(Person employee){
// .. save Employee to database
}
}

class CustomerDao : PersonDao {
public override void Save(Person customer){
// .. save Customer to database
}
}

问题:

Person person = new Person{ Name="Frank" };
Person employee = new Employee{ Name="Alice", EmployeeID=5678};
Person customer = new Customer{ Name="Nellie", CustomerID=123};

PersonDao personDao = new PersonDao();
EmployeeDao employeeDao = new EmployeeDao();
CustomerDao customerDao = new CustomerDao();

List<Person> people = new List<Person>();
people.Add(person);
people.Add(employee);
people.Add(customer);

foreach (Person person in people){
if (person.GetType() == typeof(Person))
personDao.Save(person);
else if (person.GetType() == typeof(Customer))
customerDao.Save(person);
else if (person.GetType() == typeof(Employee))
employeeDao.Save(person);
}

问题详情:查看 foreach 循环的内容,询问每个对象是什么类型,这似乎与多态性的思想完全相反。我的第一 react 是为每个 Person 类提供 Save() 本身的能力。但是,这对类(class)来说似乎责任太大了。

我想也可能有一些中间类将这个分支隐藏在别处,但我很难弄清楚如何在没有任何 if/else 的情况下做到这一点。

我在这里缺少什么设计模式或架构失败?如果 Person 的子类需要由单独的类保存或以其他方式操作,您将如何对此进行编程?

最佳答案

通常此代码没有任何问题,除非它没有散布在您的代码库中。在这种情况下,最好以某种方式进行重构。

执行此操作的自然 OOP 方法 — 将虚拟 Save 方法添加到 Person 类并在所有后代中覆盖它。但是这样做显然会违反多项设计原则。

因此,有一个 Visitor模式,它允许您向现有数据结构添加自定义操作。

它可以针对您的场景以这种方式实现。

public interface IPersonVisitor
{
void Visit(Person person);
void Visit(Employee employee);
void Visit(Customer customer);

/*Visit method overload for every item in hierarchy*/
}

在层次结构基类中接受访问者的方法

public class Person
{
public string Name;

public virtual void Accept(IPersonVisitor visitor)
{
visitor.Visit(this);
}
}

在所有后代中覆盖

public class Employee : Person
{
public int EmployeeID;

public override void Accept(IPersonVisitor visitor)
{
visitor.Visit(this);
}
}

以及每种类型操作的实际实现

public class DaoPersonVisitor: IPersonVisitor
{
public void Visit(Person person)
{
var personDao = new PersonDao();
personDao.Save(person);
}

public void Visit(Employee employee)
{
var employeeDao = new EmployeeDao();
employeeDao.Save(employee);
}

public void Visit(Customer customer)
{
var customerDao = new CustomerDao();
customerDao.Save(customer);
}
}

那么你可以这样调用它

var person = new Employee();
person.Accept(new DaoPersonVisitor());

过载解析将发挥作用,DaoPersonVisitor.Visit(Emploee) 将被调用。

当我们想向层次结构中添加一些特定操作时,访问者是一个很好的选择,但对于您的情况来说,它看起来有点矫枉过正,因为访问者本身不包含任何特定逻辑——选择适当的 DAO 对象,仅此而已。

可能,将 DAO 选择逻辑移动到一个地方就足够了,因为它会删除所有重复项。

您的 DAO 类型需要接口(interface)或基类,并在所有后代中实现它。

public interface IPersonDao
{
void Save(Person person);
}

还有一个返回适当 DAO 的工厂类(或者只是一个方法,如果这个逻辑可以放在某些类边界中的话)

public static class PersonDaoFactory
{
public static IPersonDao Create(Person person)
{
if (person is Employee)
{
return new EmployeeDao();
}

if (person is Customer)
{
return new CustomerDao();
}

/* cases for all the other object in hierarchy */

throw new NotSupportedException($"Can't create DAO for '{person.GetType().FullName}'. Type is not supported.");
}
}

然后你可以简单地在所有地方调用这个方法,DAO 应该根据人的类型来选择——这意味着没有重复。

关于c# - 给定具有相同基类的不同类型对象列表,一个完全独立的类如何在没有 if/else 逻辑的情况下操作每个对象?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50331571/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com