使用多态传递值对象是一种不好的做法吗?

时间:2012-08-30 17:43:48

标签: c# design-patterns polymorphism

public interface IRecord
{

}

public class BirthRecord : IRecord
{
    public string CityOfBirth;
    public Date DateOfBirth;
    public BirthRecord(string cityOfBirth, Date dateOfBirth)
    {
        // assign these to properties
    }
}

public class CarRecord : IRecord
{
    public Color Color;
    public string Manufacturer;
    public CarRecord(Color color, string manufacturer)
    {
        // assign these to properties
    }
}

public interface IAccount
{
    public List<IRecord> Records { get; set; }
}

public class Client
{
    public void ProcessAccount(IAccount account)
    {
        foreach(IRecord record in account.Records)
        {
            if(record is CarRecord)
                handleCarRecord((CarRecord)record);
            else if(record is BirthRecord)
                handleBirthRecord((BirthRecord)record);
        }
    }
}

所以,当你到达客户端并想要处理价值对象时,你必须做各种凌乱的类型检查和铸造 - 这是一个可接受的模式还是我犯了一个更基本的设计错误?如果没有其他OOD原则,这似乎违反了OCP。还有其他选择吗?

1 个答案:

答案 0 :(得分:2)

您提出的方法称为访问者模式,但访问者用于在复杂数据结构上创建抽象算法,以便您拥有模板算法并只提供它的具体实现。

public abstract class AbstractAccountVisitor {     

  public void ProcessAccount(IAccount account)     
  {          
     foreach(IRecord record in account.Records)         
     {             
         if(record is CarRecord)                 
             handleCarRecord((CarRecord)record);             
         else if(record is BirthRecord)                 
             handleBirthRecord((BirthRecord)record);         
     }     
  } 

  public abstract void handleCarRecord( CarRecord record );
  public abstract void handleBirthRecord( BirthRecord record );

} 

允许你拥有

public class ConcreteAccountVisitor : AbstractAccountVisitor {

    public override handleCarRecord( CarRecord record ) {
       // do something concrete with carrecord
    }

    public override handleBirthRecord( BirthRecord record ) {
       // do something concrete with birthrecord
    }

}

// client
AbstractAccountVisitor visitor = new ConcreteAccountVisitor();

visitor.ProcessAccount( account );

请注意,通过将算法的核心封装在基本访问者中,您可以通过覆盖处理特定类型记录的方法来自定义处理。

另请注意,您只需向您的班级介绍处理,而不是提供访问者:

public interface IRecord {
    void Operation();
}            

public class AccountProcessor {     

  public void ProcessAccount(IAccount account)     
  {          
     foreach(IRecord record in account.Records)         
     {             
         record.Operation();
     }     
  } 
} 

当您的对象上的可能操作列表已知时,建议使用这种更简单的方法,以便您可以在接口的合同中引入所有操作。另一方面,访问者允许您在班级/界面上引入任意数量的操作,因为您只需提供新的具体访问者。