Is polymorphism passing values ​​around objects using bad practice?

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 process value objects, you have to do all kinds of random check and casting - is this an acceptable template or am I making a more fundamental design mistake? This seems to violate OCP, if not other OOD principles. Are there any alternatives?

0
source share
1 answer

Your approach is called a visitor’s template, but the visitor is used to create abstract algorithms over complex data structures in order to have a template algorithm and simply 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 algorithm core in the base visitor, you can customize the processing by simply overriding the methods that process certain types of records.

Note that instead of providing the visitor, you can simply enter the processing into your class:

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

This simpler approach is recommended when the list of possible operations on your objects is known, so that you can enter all the operations in the interface contract. On the other hand, the visitor allows you to enter an arbitrary number of operations on your class / interface, because you simply provide new specific visitors.

+2
source

All Articles