tags:

views:

101

answers:

2

Assuming an Order(Aggregate class) with Credit Card class as a property. Depending on the state of Order I want to change the access modifier of the Credit Card class properties. For example: if Order state = Order.NewOrder, then allow Credit Card properties to be modifiable, however if Order state = Order.CompletedOrder, I do not want to allow the properties in Credit Card to be settable.

I want to be able to check at compile time vs run-time.

The only solution I thought of was to create 2 classes inheriting from the same parent class; one that allows properties to be settable the other not, but it gets hairy very quickly if there is a state that require some properties to be settable and others not.

Any ideas are greatly appreciated.

+2  A: 

Another way to solve this problem is in 'Design Patterns' pg.208-209 as a Protection Proxy.

The Proxy pattern "provides a surrogate or placeholder for another object to control access to it." You put a protection proxy version of the object in place of the actual object and access logic in the protection proxy returns read-only data, but checks when write is attempted.

Mark Rogers
The reason why you want compile time checking vs run-time checking is because you don't want a developer working with your business objects to do a set, i.e. Order.CreditCard.Number=... and then finding out they can't when an exception is thrown. Objects should always be designed so developers don't need to know to run IsChangeAllowed() etc.
traderde
"Objects should always be designed so developers don't need to know to run IsChangeAllowed()" - that depends on what you are trying to create. I did not mean to imply that developers would have to run a check, I'm merely trying to understand your problem.
Mark Rogers
The issue with this solution is the set will run if IsWritable=false and the user would not know intuitively why it didn't set properly.
traderde
I agree with traderde, the behavior would be confusing and hidden. The public interface should always behave like you expect - throwing exceptions or ignoring input would catch a lot of users off guard.Unfortunately, I don't see how to make compile time work. I think you can just try to make the usage as easy as possible (like maybe 2 Card properties that return readonly and writable interfaces to the underlying data?)
Daver
It really depends on what the situation is. It's not necessarily wrong to throw exceptions or ignore input if that is what the requirements of the project specify or if the team has any set of common conventions. The "Compile-time" checking (and how "Compile-time" checking defined) in this context is unusual, so I have a feeling traderde is pursuing a less elegant solution. That's why I'm trying to encourage him to provide more details.
Mark Rogers
+2  A: 

I'd represent the credit card object with a couple different interfaces. One would have setters and another getters. The CreditCard property on the Order class would only be the getter interface.

class CreditCardBase : ICreditCard 
{
    string Name { get; }
}

interface IWritableCreditCard : ICreditCard
{
    string Name { get; set; }
}

class WritableCreditCard : CreditCardBase, IWritableCreditCard {}

class Order
{
    private ICreditCard _card = new WritableCreditCard(); //initially...
    public ICreditCard Card { get {return _card; } }

    void OnComplete(...) { _card = new CreditCardBase(copy from _card); }
}

At run-time cast "Card" the property to the setter interface to modify the card's properties. If the order state is now in the read-only state, then the object implementing the CreditCard would have been swapped out with an implementation that only implements the getters.

I don't see how you could differentiate at compile time, but at least the normal actions would be the safe "getter only" calls, and the modify code would be a) ugly and easy to detect and b) would return null or throw if executed at the wrong time.

Daver
This is a similiar idea I had except I would use a class with only getters and all backing fields to be readonly so properties can only be set at instantiation. If there is a change necessary then I would create a new instance, copy all existing properties + changes into the new instance.
traderde
@Daver: Don't suppose you can update your example with how you would represent the `WritableCreditCard` class? I can't think how you would do it, because `new` would apply to the inherited property `get`, but there is no `set` to be made "new" of. Making it `virtual` would also fail when overriding the accessor.
Codesleuth