tags:

views:

179

answers:

3

In the following sample, the writer of the derived class will be expected to call base.Add(). If it happens 1st, the base can do one sort of code. If it happens last, the base can do another kind of logic (see sample). I doesn't seem possible to have it both ways. And easy fix would be do stop calling the base method at all because the base will never know if its being called first, last or in the middle or twice!

What is the object oriented way to deal with this? Should I just plain stop putting code into base methods because I will never know the pre and post-conditions?

EDIT: The goal is to have a business object class that does CRUD operations. The repetitious code would be moved to the base class. For example, checking to see if before adding a record, the business object's id is 0 and checking that after saving, the business object's id is >0.

namespace StackOverFlowSample
{
    class BusinessObjectBase
    {
        private bool _isNew;
        private int _id;
        public virtual void Add(string newAccount)
        {
            //Code that happens when subclasses run this method with the 
            //same signature

            //makes sense if base is called 1st
            if(_isNew && _id>0) throw new InvalidOperationException("Invalid precondition state");

            //makes sense if bae is called 2nd
            if (!_isNew && _id == 0) throw new InvalidOperationException("Invalid post condition state");
        }
    }
    class BusinessObject : BusinessObjectBase {
        public override void Add(string newAccount)
        {
            //doesn't make sense, because base will need to be called again.
            base.Add(newAccount);//pre validation, logging

            //Save newAccount to database

            //doesn't make sense, because base has already been called
            base.Add(newAccount);  //post validation, logging
        }
    }
}
+2  A: 

It's hard to follow the exact example in terms of what you want, but one solution is often to use the template pattern instead - don't ask child classes to call the base class, but provide a separate abstract method which can't do anything useful unless it calls a (non-virtual) method in the base class - or which just returns something which the template method can use to call the real method instead.

I find inheritance gets easier to understand if everything is either abstract or sealed - i.e. you've either got to override it and you can't call the base method, or you can't override it to start with. There are exceptions to this, of course, but it's a good starting point. It's a sort of functional way of looking at things as well - you may find you could actually do without inheritance, and just take appropriate delegates to provide specialization instead...

Jon Skeet
+2  A: 

If you want a safe way to introduce pre- and post condition checks, you can make Add non-virtual, and instead have another method (AddInternal or something like that) that the derived classes can (or must?) override:

namespace StackOverFlowSample
{
    abstract class BusinessObjectBase
    {
        private bool _isNew;
        private int _id;

        protected abstract void AddInternal(string newAccount);
        public void Add(string newAccount)
        {
            if(_isNew && _id>0) throw new InvalidOperationException("Invalid precondition state");
            AddInternal(newAccount);    
            if (!_isNew && _id == 0) throw new InvalidOperationException("Invalid post condition state");
        }
    }
    class BusinessObject : BusinessObjectBase {
        protected override void AddInternal(string newAccount)
        {
            //Save newAccount to database
        }
    }
}
Fredrik Mörk
Basically the same answer as mine except a minute before... Can I suggest you set the AddInternal protected?
bruno conde
@bruno: absolutely; AddInternal should be protected, that was an oversight on my part; thanks for pointing it out.
Fredrik Mörk
+1  A: 

Would it make sense something on the lines of:

class BusinessObjectBase
{
    public void Add(string newAccount)
    {
        if(_isNew && _id>0) throw new InvalidOperationException("Invalid precondition state");

        AddOperation(newAccount);

        if (!_isNew && _id == 0) throw new InvalidOperationException("Invalid post condition state");
    }

    protected void virtual AddOperation(string newAccount)
    {
        // Code from base AddOperation
    }

}
class BusinessObject : BusinessObjectBase {
    protected override void AddOperation(string newAccount)
    {
        //custom AddOperation
    }
}
bruno conde
I wish I could accept two answers! Fredrik posted a highly similar answer at about the same time.
MatthewMartin
This is the template method pattern, and it's probably your best option.
Michael Valenty