Want to avoid downcasting inside a Strategy passed to a Composite
Here is my problem domain in the financial industry :
Asset ========== Asset parent assetID + compareTo(Asset anotherAsset) Portfolio : Asset ----------------- name risk limit List stocks + compareTo(Asset anotherAsset) + composite.CompareTo(Portfolio, ComparisonRules). Stock : Asset ------- market amount company + compareTo(Asset anotherAsset) AnotherStock : Stock -------------------- someOtherProperty 开发者_运维技巧+ compareTo(Asset anotherAsset)
I have applied the composite pattern to structure *Stock*s within *Portfolio*s. I want to have a clean way of customizing the compareTo method of this composite. That is, AnotherStock will always be compared to another AnotherStock, Stocks to Stocks. This looks like the strategy pattern to me.
I would like to do something like the following (psuedocode)
differences = composite.CompareTo(anotherComposite, ComparisonRules). composite.CompareTo would be something like : ComparisonRules.Compare(this.Stocks[currentAssetID], otherComposite[currentAssetID])
ComparisonRules.Compare(Asset a, Asset b) would do something ugly like this :
if( a is Stock and b is Stock) : convert to stock and do stock-based comparison else if (a is AnotherStock and b is AnotherSTock): convert to AnotherStock
Is there a way to write ComparisonRules in such a way that I don't have to downcast yet still provide a custom ComparisonRules object?
From a rule perspective, it sounds like what you need are generics. If you define something along these lines:
public class ComparisonRule<TStock> where TStock : Stock
{
public int Compare(TStock lValue, TStock rValue)
{
...
}
}
That will guarantee that only types at or below TStock
will be accepted. For example, if I had a ComparisonRule<AnotherStock>
, then only types at or below AnotherStock
could be passed in. However, you may want to rethink your type hierarchy if you want to be able to define a rule that can compare Stock
but not AnotherStock
. You should consider having a common ancestor, but the concrete stock types should be in different inheritance trees.
In other words, you have this:
Stock
|
--------------------------
| |
OneStock AnotherStock
This would allow you to define a rule that could compare any Stock
as ComparisonRule<Stock>
, or a rule that can only compare OneStock
as ComparisonRule<OneStock>
.
This, however, doesn't help you sort out how to know which Stock
objects to pass to which rules at a higher level. For that, you'll need to be able to define a less-specific version of ComparisonRule
We can do that with an interface:
public interface IComparisonRule
{
bool CanCompare(Stock lValue, Stock rValue);
int Compare(Stock lValue, Stock rValue);
}
public abstract class ComparisonRule<TStock> : IComparisonRule where TStock : Stock
{
bool IComparisonRule.CanCompare(Stock lValue, Stock rValue)
{
return lValue is TStock && rValue is TStock;
}
int IComparisonRule.Compare(Stock lValue, Stock rValue)
{
return Compare((TStock)lValue, (TStock)rValue);
}
public abstract int Compare(TStock lValue, TStock rValue);
}
Now, strictly speaking, your question asked how to do this without downcasting, which (again, strictly speaking) isn't possible. This, however, should save you from having to do that with every implementation. For example, a simple rule to compare two AnotherStock
instances would be:
public class MyRule : ComparisonRule<AnotherStock>
{
public override int Compare(AnotherStock lValue, AnotherStock rValue)
{
return lValue.someOtherProperty.CompareTo(rValue.someOtherProperty);
}
}
At the higher level (i.e. within Portfolio
), you can simply hold on to a list of IComparisonRule
as your rules, then you can call CanCompare
and pass in two Stock
instances to see if it's a valid comparison, then pass them into Compare
in order to perform the comparison.
精彩评论