开发者

Want to avoid downcasting inside a Strategy passed to a Composite

开发者 https://www.devze.com 2023-03-29 19:17 出处:网络
Here is my problem domain in the financial industry : Asset ========== Asset parent assetID + compareTo(Asset anotherAsset)

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.

0

精彩评论

暂无评论...
验证码 换一张
取 消