问题
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);
}
}
}
So when you get to the client and want to handle the value objects you have to do all kinds of messy type checking and casting- is this an acceptable pattern or am I making a more fundamental design mistake? This seems to violate OCP if not other OOD principles. Are there any alternatives?
回答1:
The approach you present is called the Visitor Pattern however the Visitor is used to create abstract algorithms over complex data structures so that you have a template algorithm and just provide a concrete implementation of it.
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 );
}
which allows you to have
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 );
Note that by encapsulating the core of the algorithm in the base visitor, you allow to customize processing by just overriding methods which process specific types of records.
Note also that instead of providing a visitor, you can just introduce the processing to your class:
public interface IRecord {
void Operation();
}
public class AccountProcessor {
public void ProcessAccount(IAccount account)
{
foreach(IRecord record in account.Records)
{
record.Operation();
}
}
}
Such easier approach is recommended when the list of possible operations on your objects is known so that you can introduce all operations in interface's contract. On the other hand, a visitor allows you to introduce an arbitrary number of operations on your class/interface as you just provide new concrete visitors.
来源:https://stackoverflow.com/questions/12202561/is-passing-around-value-objects-using-polymorphism-a-bad-practice