CSLA, Generics, and Interfaces

CSLA, Generics, and Interfaces

Old forum URL: forums.lhotka.net/forums/t/7863.aspx


Wbmstrmjb posted on Friday, October 23, 2009

I guess this is as much a C# (generics and interfaces) question as it is a CSLA question, but here goes...

I'll keep it simple. I have Contracts which have a list of Amendments. There are 2 types of Contracts (say State, Federal):

-StateContract (Contract ^StateContract^)
-FederalContract (Contract^FederalContract^)
-Contract^T^ (editable root)
-AmendmentList^T, C^ (editable child list)
-Amendment^T^ (editable child)

I have a need for Contracts to have special handling depending on the type of contract, but for the most part they all behave the same way.

Lets says there is a TaxSum property on AmendmentList that calculates differently for Federal and State and that property Tax on Amendment is also dependent on type.

I believe (maybe incorrectly) that I need to have a StateContract, StateAmendmentList, and StateAmendment (likewise for Federal) in order to implement this structure and handling.

So StateContract would inherit from Contract^T^, StateAmendmentList would inherit from AmendmentList^T, C^ and StateAmendment would inherit from Amendment.

StateAmendmentList would override a TaxSum property of AmendmentList^T^ and StateAmendment would override Tax property of Amendment^T^.

Does this sound about right? Now, do all 3 need interfaces in order to define the properties? I have been in generic, interface, csla, hell for the past few hours trying to make sense of this.

Thanks,
Mike

RockfordLhotka replied on Saturday, October 24, 2009

It sounds like you have three types, each with their own rules - and so I agree that they should be separate types.

It also sounds like you need to use them polymorphically, so they all need to have the same shape even though they have different rules.

Generics don't provide polymorphism, so neither inheritance nor generics alone solve this problem. Additionally, inheritance causes all sorts of other negative issues, and it is a best practice to favor composition over inheritance.

But interfaces provide a way out, because a non-generic interface is polymorphic and can define a consistent shape for a set of different types.

In general what this means is that you'll want to define an interface that has the shape the consumer needs to work with all your types. All your types need to implement that interface, and to work with CSLA .NET that interface (not the types, the interface) must inherit from IEditableBusinessObject.

Wbmstrmjb replied on Monday, October 26, 2009

Thank you for the reply Rocky. I am a tad confused because I thought one of the main points you try to drive home is that you should reuse functionality, not data.

In this case, I'd say 80% of the functionality of Contracts is the same (rules like can't be modified after execution, how to add amendments, etc), but that last 20% needs to be custom for each (such as parameters like fiscal year date, and calculating money spent against that contract, which is in different tables for each type because the expenses are quite different). I thought it'd be best to make a base Contract that had the 80% and then create two extended classes for the last 20% by overriding the methods to GetXYZParameter or CalculateSpentMoney, etc. In addition, I had hoped that all Contract screens would use a generic contract/interface so that the scree didn't care what it was dealing with since the 20% is not handled by the Contract management, but rather outside (like system parameters and invoices).

Is this just not practical? I must admit after trying to get all of this to work, copy/paste seems to be simpler for getting that 80% in both classes, but I thought it went against what you advocate.

Thanks,
Mike

RockfordLhotka replied on Monday, October 26, 2009

I think it is important to realize that reuse is not a goal. Maintainability is the goal. Reuse is one tool in your tool chest, but it comes with a downside called coupling. And coupling is perhaps the worst evil in all of computing. So sure, reuse is good, but only if you can get reuse without incurring too much coupling.

I don't think of property declarations as code. I use a snippet to create them and it takes me perhaps 5 seconds to write a property. That's not code, that's a trivial implementaion detail.

Business rules and algorithms, now that is code. And I certainly do try to reuse code as much as possible.

One really good way to reuse code is through normalization of behavior. Put common behavior in a class and use that behavior from other classes that need it.

Then take into account that objects should exist because they have a single responsibility (and related set of behaviors) in your use case.

What you end up with is that objects needing different rules (behaviors) should be different types. And any common behaviors should be in yet another type, so you can collaborate with that type to get the common behaviors.

If you have 3 types that are the same shape, but have different rules, then that's fine - they should be different types. And if those 3 types also have some common rules, that's fine - there should be a fourth type that contains the implementation of those common rules.

The only catch here, is that if your "common rules" need to have conditional statements (if-then or switch or whatever) so they act differently based on which object calls the rule - then it isn't a common rule at all and you've triggered the ultimate evil of coupling in the worst possible way and your reuse has no value. In fact that kind of reuse is negative because the coupling is so bad it outweighs any potential benefit of the reuse.

Wbmstrmjb replied on Monday, October 26, 2009

I agree that Properties are not code. If something I said lead you to believe otherwise, my mistake. I don't even think CRUD DataPortal calls are really code since I codegen 90% of it. I agree that the business rules and algorithms are the real code. That is what I was trying to reuse. I guess I was trying to build into my Contract classes (through a base class) the common functionality.

For instance, every Contract no matter what type, has an Execution Date and no changes can be made post-Execute. I think that you are suggesting that the business rule for calculating this would go into an external class and each class would call this external class and pass itself to the external class so that they could be evaluated? This sounds reasonable, just a shift from how I normally look at things.

I was thinking more like:

class Base
{
virtual Calculate() {}

// use Calculate elsewhere
}

class A : Base
{
override Calculate() { return 0; }
}

class B : Base
{
override Calculate() { return 1; }
}

But maybe this is just not practical. I will try your suggestions, or at least how I interpret them :), and see how it goes.

Thanks,
Mike

RockfordLhotka replied on Monday, October 26, 2009

You can certainly use inheritance to get behavior (not properties or fields) from a base class. I still think you should favor composition over inheritance, but inheritance is a valid approach.

However, you are describing using inheritance to define a contract that is implemented differently in each subclass. In which case what are you reusing? Essentially nothing.

The reason for defining a contract (method signature) in a base class is to achieve polymorphic behavior, not reuse. The same is true for the use of interfaces. And there's nothing wrong with doing this, as long as your goal is polymorphism, not reuse.

All that said, ultimately you'll probably use "Calculate()" as a business rule method, in which case you don't need polymophism either, because the specific subclass implementation will be what's referenced by each subclass as it associates the rules with your properties or rulesets.

Wbmstrmjb replied on Monday, October 26, 2009

I think I made a huge mistake by using my "Contract" class as an example! :) When I said Contract, it meant a Contract like a piece of paper between two parties, not like a "contract" that is created by the use of an interface.

In regards to Calculate(), you are correct, it would be implemented 2 different ways. But the result of Calculate(), say a decimal, would be used commonly in other business rules like CanISpendMoreMoneyAgainstThisContract (not really a method)? Both Contracts would have the same rule for CanSpend... except one would use the result from Calculate() of one Contract type and the other from another. Thus the base class which implemented CanSpend... could provide the rest of the implementation except for the Calculate() piece.

But now I think your external collaboration will work just as well if I pass the results of the specific needs of each class to the external shared class that defines the common functionality.

In any event, I am going the route of splitting it out and seeing where I land.

Thanks,
Mike

Wbmstrmjb replied on Monday, October 26, 2009

One last question with regards to this method: Save, and how to implement correctly.

Save() obviously is part of BusinessBase and has a return type of the generic "T". If you make an interface to handle 2 CSLA BOs, how do you specify that each one has Save() since setting the return type to IMyObject does not compile since the signatures are different?

My work around (and maybe it's correct) was to not have Save() as part of the interface, but rather SaveMyObject() that has a return type of IMyObject. Then the implementation of SaveMyObject just wraps Save() with a cast to (IMyObject).

Is this appropriate or do I need another lesson? :)

Thanks,
Mike

RockfordLhotka replied on Monday, October 26, 2009

CSLA already has an ISavable interface to make it possible to build
polymorphic code (typically in the UI) that saves arbitrary editable
objects.

I thought you were talking about child objects in a collection though, in
which case you save the collection, not individual child objects.

Wbmstrmjb replied on Tuesday, October 27, 2009

Ah, I missed the ISavable. That makes things a little simpler.

The objects are not children. Contract is an editable root. But there are 2 different types of Contracts and each need to have their own business rules. The UI is identical for both types because the rules that are different are affected by other areas of the application. So our thought was to handle both Contracts with 1 UI.

The problem we are finding now is how we bypass handling security checks when the interface IContract can't have statics for CanGetObject, CanAddObject, etc? I think if we nail security, we'll be good to go.

Thanks,
Mike

RockfordLhotka replied on Tuesday, October 27, 2009

You are using the per-type authz rules system?

Your UI code can always ask CSLA directly about per-type authz rules:

if (Csla.Security.AuthorizationRules.CanGetObject(typeof(blah)))
// do whatever

Wbmstrmjb replied on Tuesday, October 27, 2009

Oh, great! I was unaware of that feature. I will see about retrofitting our security with that instead of using reflection.

Thanks,
Mike

Wbmstrmjb replied on Tuesday, October 27, 2009

What version of CSLA is that in Rocky? There are no statics hanging off AuthorizationRules in my version (3.0.5) and instantiating an AuthRules object only has IsRead/WriteAllowed, not CRUD.

RockfordLhotka replied on Tuesday, October 27, 2009

Sorry, those features were added in 3.5 or 3.6.

Wbmstrmjb replied on Tuesday, October 27, 2009

So are my options limited for implementing this? Either move to 3.5 or not have security?

RockfordLhotka replied on Tuesday, October 27, 2009

Either move to 3.7.1 (I'd skip 3.5 and 3.6 at this point), or you'll need to
invent your own model for polymorphic per-type authorization.

You may be able to back-port the concept from 3.7 into 3.0.5 - I think the
changes were fairly isolated to the Csla.Security namespace (other than the
AddObjectAuthorizationRules() method support in BusinessBase, etc).

Copyright (c) Marimer LLC