Builder design pattern with inheritance: is there a better way? - c#

I'm creating a series of builders to clean up the syntax which creates domain classes for my mocks as part of improving our overall unit tests. My builders essentially populate a domain class (such as a Schedule) with some values determined by invoking the appropriate WithXXX and chaining them together.
I've encountered some commonality amongst my builders and I want to abstract that away into a base class to increase code reuse. Unfortunately what I end up with looks like:
public abstract class BaseBuilder<T,BLDR> where BLDR : BaseBuilder<T,BLDR>
where T : new()
{
public abstract T Build();
protected int Id { get; private set; }
protected abstract BLDR This { get; }
public BLDR WithId(int id)
{
Id = id;
return This;
}
}
Take special note of the protected abstract BLDR This { get; }.
A sample implementation of a domain class builder is:
public class ScheduleIntervalBuilder :
BaseBuilder<ScheduleInterval,ScheduleIntervalBuilder>
{
private int _scheduleId;
// ...
// UG! here's the problem:
protected override ScheduleIntervalBuilder This
{
get { return this; }
}
public override ScheduleInterval Build()
{
return new ScheduleInterval
{
Id = base.Id,
ScheduleId = _scheduleId
// ...
};
}
public ScheduleIntervalBuilder WithScheduleId(int scheduleId)
{
_scheduleId = scheduleId;
return this;
}
// ...
}
Because BLDR is not of type BaseBuilder I cannot use return this in the WithId(int) method of BaseBuilder.
Is exposing the child type with the property abstract BLDR This { get; } my only option here, or am I missing some syntax trick?
Update (since I can show why I'm doing this a bit more clearly):
The end result is to have builders that build profiled domain classes that one would expect to retrieve from the database in a [programmer] readable format. There's nothing wrong with...
mock.Expect(m => m.Select(It.IsAny<int>())).Returns(
new Schedule
{
ScheduleId = 1
// ...
}
);
as that's pretty readable already. The alternative builder syntax is:
mock.Expect(m => m.Select(It.IsAny<int>())).Returns(
new ScheduleBuilder()
.WithId(1)
// ...
.Build()
);
the advantage I'm looking for out of using builders (and implementing all these WithXXX methods) is to abstract away complex property creation (automatically expand our database lookup values with the correct Lookup.KnownValues without hitting the database obviously) and having the builder provide commonly reusable test profiles for domain classes...
mock.Expect(m => m.Select(It.IsAny<int>())).Returns(
new ScheduleBuilder()
.AsOneDay()
.Build()
);

All I can say is that if there is a way of doing it, I want to know about it too - I use exactly this pattern in my Protocol Buffers port. In fact, I'm glad to see that someone else has resorted to it - it means we're at least somewhat likely to be right!

I know this is an old question, but I think you can use a simple cast to avoid the abstract BLDR This { get; }
The resulting code would then be:
public abstract class BaseBuilder<T, BLDR> where BLDR : BaseBuilder<T, BLDR>
where T : new()
{
public abstract T Build();
protected int Id { get; private set; }
public BLDR WithId(int id)
{
_id = id;
return (BLDR)this;
}
}
public class ScheduleIntervalBuilder :
BaseBuilder<ScheduleInterval,ScheduleIntervalBuilder>
{
private int _scheduleId;
// ...
public override ScheduleInterval Build()
{
return new ScheduleInterval
{
Id = base.Id,
ScheduleId = _scheduleId
// ...
};
}
public ScheduleIntervalBuilder WithScheduleId(int scheduleId)
{
_scheduleId = scheduleId;
return this;
}
// ...
}
Of course you could encapsulate the builder with
protected BLDR This
{
get
{
return (BLDR)this;
}
}

This is a good implementation strategy for C#.
Some other languages (can't think of name of research language I've seen this in) have type systems that either support a covariant "self"/"this" directly, or have other clever ways to express this pattern, but with C#'s type system, this is a good (only?) solution.

Related

How to avoid casting to a specific interface

Background info
I have a set of interfaces/classes as follows. For the sake of simplicity imagine more properties, collections etc.
interface IMaster
{
//Some properties
}
interface IB : IMaster
{
string PropOnA { get; set }
}
interface IC : IMaster
{
string PropOnB { get; set }
}
class B : IB
class C : IC
...
These contracts were designed to store data(which is held in a slightly different format in each case). There is a lot of code that uses these contracts to get the data, format it, process it, write etc.
We have developed an entire library that does not see the concrete implementations(B,C) of any of these contracts by inverting control and allow the user to use our 'default implementations' for each contract or just loading in their own. We have registry where the user can register a different implementation.
To this end I have implemented a kind of strategy pattern where there exists a strategy for each contract type based on the task at hand. For the sake of simplicity lets say the task is writing, in reality it is much more complicated.
interface IWriteStrategy
{
public Write(IMaster thing);
}
class WriterA : IWriteStrategy
class WriterB : IWriteStrategy
etc
The above concrete strategies are also never 'seen' in our library, the client must register their own implementation or our default version.
Design flaw??
I am not liking the cast in every strategy that is now necessary.
public classWriterA : IWriteStrategy
{
public void Write(IMaster thing)
{
if(thing is IA thingA)
//do some work
}
}
public classWriterB : IWriteStrategy
{
public void Write(IMaster thing)
{
if(thing is IB thingB)
//do some work
}
}
What we want to do is be able to loop through a list of IMaster objects and run some operations.
foreach(var thing in Things)
{
var strategy = GetStrategy(thing.GetType()); //this gets the strategy object from our `registry` if one exists
strategy.Execute(thing);
}
The above design allows this but there seems to be a flaw which I cant for the life of me spot a solution to. We have to cast to the specific interface within each strategy implementation.
I have tried with generics, but just cant seem to nail it.
Question
What would be a better way of designing this to avoid the cast but still be able to loop through a list of IMaster things and treat them the same? Or is the cast absolutely necessary here?
I am trying to follow a SOLID design but feel the cast is messing with this as the client implementing the strategies will have to do the cast in order to get anything to work within the Write method.
[Edit]
I have updated the classes implementing the IWriteStrategy.
If you rarely add new IMaster specializations, but often add new operations OR need to make sure operation providers (e.g writer) needs to support ALL specializations then the Visitor Pattern is a perfect fit.
Otherwise you basically need some kind of service locator & registration protocol to map operation providers/strategies to IMaster specializations.
One way you could do it is define generic interfaces such as IMasterWriter<T> where T:IMaster which can then be implemented like IBWriter : IMasterWriter<IB> which defines the mapping.
From that point you only need a mechanism that uses reflection to find a specific IMasterWriter implementor for a given type of IMaster and decide what to do if it's missing. You could scan assemblies early to detect missing implementations at boot rather than failing later too.
Maybe it is appropriate to use Strategy pattern and just give an implementation and execute it. Let me show an example.
interface IMaster
{
void ExecuteMaster();
}
class MasterOne : IMaster
{
public void ExecuteMaster()
{
Console.WriteLine("Master One");
}
}
class MasterTwo : IMaster
{
public void ExecuteMaster()
{
Console.WriteLine("Master Two");
}
}
and
interface IWriteStrategy
{
void Write(IMaster thing);
}
class WriterA : IWriteStrategy
{
public void Write(IMaster thing)
{
Console.WriteLine("Writer A");
thing.ExecuteMaster();
}
}
class WriterB : IWriteStrategy
{
public void Write(IMaster thing)
{
Console.WriteLine("Writer B");
thing.ExecuteMaster();
}
}
and code to execute:
static void Main(string[] args)
{
List<IWriteStrategy> writeStrategies = new List<IWriteStrategy>()
{
new WriterA(),
new WriterB()
};
List<IMaster> executes = new List<IMaster>()
{
new MasterOne(),
new MasterTwo()
};
for (int i = 0; i < writeStrategies.Count(); i++)
{
writeStrategies[i].Write(executes[i]);
}
}
what about this, you will have all your casts in one strategy factory method:
public interface IWriterStrategy
{
void Execute();
}
public class WriterA : IWriterStrategy
{
private readonly IA _thing;
public WriterA(IA thing)
{
_thing = thing;
}
public void Execute()
{
Console.WriteLine(_thing.PropOnA);
}
}
public class WriterB : IWriterStrategy
{
private readonly IB _thing;
public WriterB(IB thing)
{
_thing = thing;
}
public void Execute()
{
Console.WriteLine(_thing.PropOnB);
}
}
public static class WriterFactory
{
public static List<(Type Master, Type Writer)> RegisteredWriters = new List<(Type Master, Type Writer)>
{
(typeof(IA), typeof(WriterA)),
(typeof(IB), typeof(WriterB))
};
public static IWriterStrategy GetStrategy(IMaster thing)
{
(Type Master, Type Writer) writerTypeItem = RegisteredWriters.Find(x => x.Master.IsAssignableFrom(thing.GetType()));
if (writerTypeItem.Master != null)
{
return (IWriterStrategy)Activator.CreateInstance(writerTypeItem.Writer, thing);
}
throw new Exception("Strategy not found!");
}
}
public interface IMaster
{
//Some properties
}
public interface IA : IMaster
{
string PropOnA { get; set; }
}
public interface IB : IMaster
{
string PropOnB { get; set; }
}
public interface IC : IMaster
{
string PropOnC { get; set; }
}
public class ThingB : IB
{
public string PropOnB { get => "IB"; set => throw new NotImplementedException(); }
}
public class ThingA : IA
{
public string PropOnA { get => "IA"; set => throw new NotImplementedException(); }
}
public class ThingC : IC
{
public string PropOnC { get => "IC"; set => throw new NotImplementedException(); }
}
internal static class Program
{
private static void Main(string[] args)
{
var things = new List<IMaster> {
new ThingA(),
new ThingB()//,
//new ThingC()
};
foreach (var thing in things)
{
var strategy = WriterFactory.GetStrategy(thing); //this gets the strategy object from our `registry` if one exists
strategy.Execute();
}
}
}

Abstract class constructor call overridable method

I try to create a good testable repository class to use with Moq. I don't want duplicate my selector methods (GetAll, Get, ...). My implementation works fine but SonarSource reports an error RSPEC-1699 Does anyone know of a better implementation?
var areas = new Area[] { ... };
var areaRepositoryMock = new Mock<BaseAreaRepository>() { CallBase = true };
areaRepositoryMock.Setup(m => m.Initialize()).Returns(areas);
Base Class
public abstract class BaseAreaRepository
{
protected Area[] _areas;
protected BaseAreaRepository()
{
this._areas = this.Initialize();
}
public abstract Area[] Initialize();
public Area[] GetAll()
{
return this._monitoredAreas;
}
public Area Get(int id)
{
return this._areas.FirstOrDefault(o => o.Id.Equals(id));
}
}
MyAreaRepository
public class MyAreaRepository : BaseAreaRepository
{
public override Area[] Initialize()
{
return //Load data from an other source
}
}
The RSPEC-1699 Constructors should only call non-overridable methods doens't have anything with the unit tests it will remain there regardless how you are going to test it.
Does anyone know of a better implementation?
I would like to propose another approach in order to avoid this violation and make your code even more testable.
The idea is instead of the base class use composition and DI principle.
public interface IAreaContext
{
Area[] GetAreas();
}
public class AreaRepository
{
private IAreaContext _areaContext;
protected BaseAreaRepository(IAreaContext areaContext)
{
_areaContext = areaContext;
}
public Area[] GetAll()
{
return _areaContext.GetAreas();
}
}
Then you could define multiple implementations of IAreaContext and injext:
public class MyAreaContext : IAreaContext
{
public Area[] GetAreas()
{
return //Load data from an other source
}
}
public class MyOtherAreaContext : IAreaContext
{
public Area[] GetAreas()
{
return //Load data from an other source
}
}
Now when you have this setup repository could be easily testable for different behaviors of the context itself. This is just an example to demonstrate idea:
//Arrange
var context = new Mock<IAreaContext>();
context.Setup(m => m.GetAreas()).Verifiable();
var sut = new AreaRepository(context.Object);
//Act
var _ = sut.GetAll();
//Assert
context.Verify();
If you want to test just the base class, then I would create a unit test specific implementation of the class, and just provide any helper functions to test the protected ones. Basically what you have done with MyAreaRepository but as a private class within the test class.

Binding wrapper classes with ninject

I have an interface that represents a table in a 3rd party API. Each instance provides the ability to search a single table using forward-only cursors:
public interface ITable
{
string TableName { get; }
ICursor Search(string whereClause);
}
I have written a wrapper class to handle searching an ITable and returning an enumerable instead (it's a little more complex than that in reality, but sufficient for showing my issue):
public interface ITableWrapper
{
IEnumerable<object> Search(string whereClause);
}
public class TableWrapper : ITableWrapper
{
private ITable _table;
public TableWrapper(ITable table)
{
_table = table;
}
public IEnumerable<Row> Search(string whereClause)
{
var cursor = _table.Search(whereClause);
while(cursor.Next())
{
yield return cursor.Row;
}
}
}
I then have several repository classes that should have a table wrapper injected:
public class Table1Repository
{
private ITableWrapper _table;
public Table1Reposiroty(ITableWrapper table)
{
_table = table;
}
//repository methods to actually do things
}
Since each table will have its own wrapper, and repositories need the correct table injecting, my thought was to use named bindings on the tables and wrappers so that ninject provides the correct instance. Thus the above class would have NamedAttribute applied to the constructor argument, and the binding would be as follows:
public void NinjectConfig(IKernel kernel, ITableProvider provider)
{
Bind<ITable>().ToMethod(ctx => provider.OpenTable("Table1")).Named("Table1").InSingletonScope();
Bind<ITableWrapper>().ToMethod(ctx => new TableWrapper(ctx.ContextPreservingGet<ITable>("Table1"))).Named("Table1Wrapper").InSingletonScope();
}
My questions are:
Is there a cleaner way to express this binding? I was thinking maybe a way to bind ITableWrapper once and have a new instance returned for each named ITable, with the repository constructor parameter attribute picking the named ITable for which it wants the ITableWrapper.
If the ITable should never be used by anything, and everything should always use ITableWrapper, is it ok (or even recommended) to bind just ITableWrapper and have that combine both ToMethod contents:
public void NinjectConfig(IKernel kernel, ITableProvider provider)
{
Bind<ITableWrapper>().ToMethod(ctx => new TableWrapper(provider.OpenTable("Table1"))).Named("Table1Wrapper").InSingletonScope();
}
There's no Ninject-built-in way to provide metadata to Ninject by attribute. The only thing it supports is the ConstraintAttribute (and the NamedAttribute as a subclass). This can be used to select a specific binding, but it can't be used to provide parameters for a binding.
So, in case you don't want to add a lot of code, the easiest and most concise way is along of what you suggested yourself:
public static BindTable(IKernel kernel, ITableProvider tableProvider, string tableName)
{
kernel.Bind<ITableWrapper>()
.ToMethod(ctx => new tableWrapper(tableProvider.OpenTable(tableName))
.Named(tableName);
}
(I've used the same string-id here for both table name and ITableWrapper name - this way you don't need to map them).
Also, i think it's better not to create a binding for ITable if you're not going to use it, anyway.
note: If you were to create the ITableWrapper by a factory (instead of ctor-injecting it), you could use parameters and a binding which reads the table-id from the parameter. Meaning a single binding would suffice.
Generic Solution
Now in case you're ok with adding some custom code you can actually achieve a generic solution. How? you add a custom attribute to replace the NamedAttribute which provides the table name. Plus you create a binding which reads the table name from this custom attribute. Let's say:
[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = true)]
public class TableIdAttribute : Attribute
{
public TableIdAttribute(string tableName)
{
TableName = tableName;
}
public string TableName { get; private set; }
}
let's implement an IProvider to capsule the added binding complexity (it would also work with a ToMethod binding):
internal class TableWrapperProvider : Provider<ITableWrapper>
{
private readonly ITableProvider _tableProvider;
public TableWrapperProvider(ITableProvider tableProvider)
{
_tableProvider = tableProvider;
}
protected override ITableWrapper CreateInstance(IContext context)
{
var parameterTarget = context.Request.Target as ParameterTarget;
if (parameterTarget == null)
{
throw new ArgumentException(
string.Format(
CultureInfo.InvariantCulture,
"context.Request.Target {0} is not a {1}",
context.Request.Target.GetType().Name,
typeof(ParameterTarget).Name));
}
var tableIdAttribute = parameterTarget.Site.GetCustomAttribute<TableIdAttribute>();
if (tableIdAttribute == null)
{
throw new InvalidOperationException(
string.Format(
CultureInfo.InvariantCulture,
"ParameterTarget {0}.{1} is missing [{2}]",
context.Request.Target,
context.Request.Target.Member,
typeof(TableIdAttribute).Name));
}
return new TableWrapper(_tableProvider.Open(tableIdAttribute.TableName));
}
}
and here's how we use it (example classes):
public class FooTableUser
{
public FooTableUser([TableId(Tables.FooTable)] ITableWrapper tableWrapper)
{
TableWrapper = tableWrapper;
}
public ITableWrapper TableWrapper { get; private set; }
}
public class BarTableUser
{
public BarTableUser([TableId(Tables.BarTable)] ITableWrapper tableWrapper)
{
TableWrapper = tableWrapper;
}
public ITableWrapper TableWrapper { get; private set; }
}
and here's the bindings plus a test:
var kernel = new StandardKernel();
kernel.Bind<ITableProvider>().ToConstant(new TableProvider());
kernel.Bind<ITableWrapper>().ToProvider<TableWrapperProvider>();
kernel.Get<FooTableUser>().TableWrapper.Table.Name.Should().Be(Tables.FooTable);
kernel.Get<BarTableUser>().TableWrapper.Table.Name.Should().Be(Tables.BarTable);

creating decorators. design patterns

let's say two simple decorators are defined:
// decorated object
class Product : IComponent {
// properties..
// IComponent implementation
public Decimal GetCost() {
return this.SelectedQuantity * this.PricePerPiece;
}
}
// decorators
class FixedDiscountDecorator : IComponent {
IComponent component;
// IComponent implemantation
public Decimal GetCost() {
// ...
}
public FixedDiscountDecorator(IComponent product, Decimal discountPercentage) {
// ...
}
}
class BuyXGetYFreeDiscountDecorator : IComponent {
IComponent component;
// IComponent implemantation
public Decimal GetCost() {
// ...
}
// X - things to buy
// Y - things you get free
public BuyXGetYFreeDiscountDecorator(IComponent product, Int32 X, Int32 Y) {
// ...
}
}
These decorators have different constructors' signature (parameter list). I was looking for a pattern to apply to construct decorators like it could be with factory pattern. I mean I put a string and get a decorator instance.
As a result I want to simply apply a chain of decorators to a given product:
var product = new SimpleProduct {
Id = Guid.NewGuid(),
PricePerPiece = 10M,
SelectedQuantity = 10,
Title = "simple product"
};
var itemsToApplyTheDiscount = 5;
var itemsYouGetFree = 2;
var discountPercentage = 0.3M;
var discountA = new BuyXGetYFreeDecorator(product, itemsToApplyTheDiscount, itemsYouGetFree);
var discountB = new FixedDiscountDecorator(discountA, discountPercentage);
This can be resolved using IOC container or something similar. Some of the containers popped up in my head is Unity, Windsor and SimpleInjector. I will leave the IOC container to other answers since I have no experience there.
However, I really wonder why you inject a native value.
Seeing about how the class will be used, it feels weird to have an injected value like discount percentage or x buy y free item injected into the constructor.
What if the user put 10 (as percent) instead of 0.1 (as decimal) as the discount parameter? It makes ambiguity. Additionaly, if you add checks at the constructor, you give another responsibility to the class, violating SRP.
I suggest to add a DTO such as DiscountPercentValue or BuyXGetYFreeValue. Moreover, I prefer the value of discount is being set as a context or there is a repository to be injected for it. Otherwise, someday you will need factories to handle if-else business rules related to discounts.
EDIT1:
Usually I keep the constructor validation as null checks only. Other validation than that can be considered violation.
As for the repository things, I imagine some interfaces like these:
public interface IDiscountPercentageProvider{
DiscountValue Get();
}
public interface IBuyXGetYFreeValueProvider{
BuyXGetYFreeValue Get();
}
Then in your service class, you can use something like this:
class FixedDiscountDecorator : IComponent {
IComponent component;
// IComponent implemantation
IDiscountPercentageProvider discountPercentageProvider;
public Decimal GetCost() {
DiscountValue discount = discountPercentageProvider.Get();
// ...
}
public FixedDiscountDecorator(IComponent product
, IDiscountPercentageProvider discountPercentageProvider) {
// ... just null checks here
}
}
This may be complicated at first. However, it provides better API design (no ambiguity now when using decorator). Using this, you can create a DiscountValue as a class that protects its invariants, making it safer to be used in other classes.
In your example you do show a chain of decorators applied to a given product, namely:
var discountA = new BuyXGetYFreeDecorator(product, itemsToApplyTheDiscount, itemsYouGetFree);
var discountB = new FixedDiscountDecorator(discountA, discountPercentage);
Is the question, then, what are some patterns that could be used to change the state of product governed by a specified property, building from your code above? Using your example you have and limiting my scope to determining the product Cost:
public interface IComponent
{
decimal GetCost { get; set; }
}
I would create a product class representing IComponent
public class Product
{
public IComponent Price { get; set; }
}
You may have a "default" implementation in addition to the decorator classes
public class BasePrice : IComponent
{
private Decimal _cost;
public decimal GetCost //as a property maybe use Cost with get; set; in IComponent
{
get { return _cost; }
set { _cost = value; }
}
}
I like your two decorator classes which use IoC and implement variations of cost (GetCost()) from IComponent.
To this point I have not really added anything, only a base price class. What I might do next is use an abstract class and defer specific operations to subclasses, as represented in the Template Method pattern. My concrete classes would inherit from this base class. The concrete class created will depend upon the action type passed into Factory method pattern, the class referred to below as WarrantyProcessFactory.
You mentioned using app.config ... I like enums which I would use to specify the action types to be applied to product. So let's say I want to have actions tied to the product's warranty so I can process products based on this.
public enum WarrantyAction
{
RefundProduct = 0,
ReplaceProduct = 1
}
I will use a warranty request class to represent compensation requests by customers for defective products
public class WarrantyRequest
{
public WarrantyAction Action { get; set; }
public string PaymentTransactionId { get; set; }
public decimal PricePaid { get; set; }
public decimal PostageCost { get; set; }
public long ProductId { get; set; }
public decimal AmountToRefund { get; set; }
}
Finally, I could implement the abstract template method that will be overridden by concrete classes representative of the warranty action enums
public abstract class WarrantyProcessTemplate
{
protected abstract void GenerateWarrantyTransactionFor(WarrantyRequest warrantyRequest);
protected abstract void CalculateRefundFor(WarrantyRequest warrantyRequest);
public void Process(WarrantyRequest warrantyRequest)
{
GenerateWarrantyTransactionFor(warrantyRequest);
CalculateRefundFor(warrantyRequest);
}
}
The class and the first two methods are abstract and are required to be implemented by a subclass. The third method simply calls in the two abstract methods and passes a WarrantyRequest entity as an argument.
Cases in which the client wants a refund
public class RefundWarrantyProcess : WarrantyProcessTemplate
{
protected override void GenerateWarrantyTransactionFor(WarrantyRequest warrantyRequest)
{
// Code to determine terms of the warranty and devalutionAmt...
}
protected override void CalculateRefundFor(WarrantyRequest warrantyRequest)
{
WarrantyRequest.AmountToRefund = warrantyRequest.PricePaid * devalutionAmt;
}
}
Cases in which the client wants a replacement product
public class ReplaceWarrantyProcess : WarrantyProcessTemplate
{
protected override void GenerateWarrantyTransactionFor(WarrantyRequest warrantyRequest)
{
// Code to generate replacement order
}
protected override void CalculateRefundFor(WarrantyRequest warrantyRequest)
{
WarrantyRequest.AmountToRefund = warrantyRequest.PostageCost;
}
}
public static class WarrantyProcessFactory
{
public static WarrantyProcessTemplate CreateFrom(WarrantyAction warrantyAction)
{
switch (warrantyAction)
{
case (WarrantyAction.RefundProduct):
return new RefundWarrantyProcess();
case (WarrantyAction.ReplaceProduct):
return new ReplaceWarrantyProcess();
default:
throw new ApplicationException(
"No Process Template defined for Warranty Action of " +
warrantyAction.ToString());
}
}
}
Another pattern I would consider is the Strategy Method pattern. In this case a Context class defers all calculations to a "ConcreteStrategy" referenced by its abstract class or interface. I found Scott Millet's book "Professional ASP.NET Design Patterns" as a useful resource and often return to it.
I am open to comments and criticism.

design pattern to remove multiple if/else clauses with related objects

I have inherited the following (terrible) code and am wondering how best to refactor it.
There are large if/else clauses all over the codebase, one of which is similar to below :
public class BaseResultItem
{
public int Property1 { get; set; }
}
public class ResultItem1 : BaseResultItem
{
public int Property2 { get; set; }
}
public class ResultItem2 : BaseResultItem
{
public int Property3 { get; set; }
}
public class BaseHistoryItem
{
public int Property1 { get; set; }
}
public class HistoryItem1 : BaseHistoryItem
{
public int Property2 { get; set; }
}
public class HistoryItem2 : BaseHistoryItem
{
public int Property3 { get; set; }
}
public class HistoryBuilder
{
public BaseHistoryItem BuildHistory(BaseResultItem result)
{
BaseHistoryItem history = new BaseHistoryItem
{
Property1 = result.Property1
};
if (result is ResultItem1)
{
((HistoryItem1)history).Property2 = ((ResultItem1)result).Property2;
}
else if (result is ResultItem2)
{
((HistoryItem2)history).Property3 = ((ResultItem2)result).Property3;
}
return history;
}
}
Note that this is a simplified example and there are many more classes involved in the actual code. There are similar if/else clauses all over the place.
I have been looking at the abstract factory pattern but I am having some problems.
Basically I am assuming that to avoid the if/else problems I need to pass the actual dervied types around. So BuildHistory should not use base types and maybe there should be multiple methods, one per derived type?
If you can't change the DTO classes perhaps you can try to subclass HistoryBuilder to deal with the different subclasses. Then you use the appropriate HistoryBuilderX to create a HistoryItem from a ResultItem. Then the question is how to get the appropriate HistoryBuilderX for the ResultItem supplied.
Still, if you can't change the BaseResultItem class to include a GetBuilder function you need to use some if..else if.. construct that inspects the classtypes of your ResultItems.
Or you create a Registry where every ResultItem class is registered with its corresponding HistoryBuilderX class. But that might be overkill.
The general 'design pattern' is simply to use object orientation with polymorphism instead of type checks. Thus: a BuildHistory method inside BaseResultItem, overridden by descendants.
Any code which checks the concrete type of an object smells (in a refactoring sense). Supporting different behaviours for different types is what OO is about.
Use polymorphism to remove the type checks.
if (result is ResultItem1)
{
((HistoryItem1)history).Property2 = ((ResultItem1)result).Property2;
}
Becomes then something like
result.addToHistory( history );
If for some reason, you don't want to scatter the logic in the item classes, have a look at the visitor pattern. In this case, you have something like:
public class Visitor {
History history;
public visit ( ResultItem1 item ) { ... }
public visit ( ResultItem2 item ) { ... }
...
}
public class ResultItem1 {
public accept( Visitor v ) { v.visit( this ); }
}
The typecheck is removed by the double-dispatch in the visitor, which is slightly more elegant.
I didn't understood exactly how the various kind of history relates to the various kind of items. So this is just a sketch of possibles direction to follow.

Categories