views:

157

answers:

4

I'm contemplating two different class designs for handling a situation where some repositories are read-only while others are read-write. (I don't foresee any need for a write-only repository.)


Class Design 1 -- provide all functionality in a base class, then expose applicable functionality publicly in sub classes

public abstract class RepositoryBase
{
    protected virtual void SelectBase() { // implementation... }
    protected virtual void InsertBase() { // implementation... }
    protected virtual void UpdateBase() { // implementation... }
    protected virtual void DeleteBase() { // implementation... }
}

public class ReadOnlyRepository : RepositoryBase
{
    public void Select() { SelectBase(); }
}

public class ReadWriteRepository : RepositoryBase
{
    public void Select() { SelectBase(); }
    public void Insert() { InsertBase(); }
    public void Update() { UpdateBase(); }
    public void Delete() { DeleteBase(); }
}

Class Design 2 - read-write class inherits from read-only class

public class ReadOnlyRepository
{
    public void Select() { // implementation... }
}

public class ReadWriteRepository : ReadOnlyRepository
{
    public void Insert() { // implementation... }
    public void Update() { // implementation... }
    public void Delete() { // implementation... }
}

Is one of these designs clearly stronger than the other? If so, which one and why?

P.S. If this sounds like a homework question, it's not, but feel free to use it as one if you want :)

A: 

I would certainly say that design 2 is the strongest. If you want to have a read-only implementation, it does not need to know anything about writing. It makes perfect sense to extend the read-only implementation with the insert, update, and delete methods. I also think this design is the one that conforms the most to the Open-Closed principle.

driis
+5  A: 

(Edit: I think Eric Petroelje offers a very nice interface-based solution in his answer. I would probably vote for his suggestion, first of all.)

From your two choices, I would clearly vote for design #2.

With design #1, I think it doesn't make sense to have a "read-only" class that internally isn't read-only at all:

  1. The read-only class is "heavier" than it needs to be.

  2. Anyone can derive from your read-only class and then call any of the base class' modification methods. At the very least, with design #1, you ought to make the read-only class sealed.

With design #2, it's much clearer than the read-only class is a reduced version (base class) of the full-featured class, or phrased differently.

stakx
Yep. This is exactly the design that I used in my projects that needed separate RW and R interfaces.
JSBangs
I think I prefer Eric's solution as well, but +1 for your point #2. It's an issue I hadn't thought of.
DanM
+14  A: 

How about a third option, closely related to the first, but using interfaces instead:

public interface IReadRepository {
    public void Select();
}

public interface IWriteRepository {
    public void Insert();
    public void Update();
    public void Delete();
}

// Optional
public interface IRepository : IReadRepository, IWriteRepository {
}

public class Repository : IRepository {
   // Implementation
}

This way the implementation is (or can be) all in one place, and the distinction is made only by which interface you are looking at.

Eric Petroelje
+1. I like this approach *very much* because a method can accurately specify what kind of access to a `Repository` it needs: If read-only access is sufficient, it demands a `IReadRepository`; if it needs write access, it demands `IWriteRepository`; if it needs read-write access, it demands a `IRepository`. One curious side-effect of this is that the definition of a property's `get` and `set` can get separated into two interfaces.
stakx
I think this is an interesting pattern, because you can enforce at least a "semantic" kind of security, by giving the objects in your program that "need write access and are cleared for write access" access to the "writeable" interfaces. I would not just think of this as a security issue (because security concerns are a database permission issue), but also as a "minimize how much interface you give various objects within the system", as a good OOP principle.
Warren P
But still, unless the domain calls for WriteOnly classes, I would link the 2 interfaces (IWriteRepository: IReadRepository) and that means the inheritance question has shifted to the interfaces.
Henk Holterman
I like this too, Eric, thanks. +1
DanM
@Henk - Exactly, you could skip the IWriteRepository and simply have the IRepository interface define the write methods. This would give you the advantages of his second design, but without requiring him to split the implementation between two classes.
Eric Petroelje
_@Henk Holtermann:_ That would probably depend on whether write-only access is actually useful, and also feasible. Concerning the latter, it's possible that an `Update` actually needs read access to accomplish its task; in that case, it would probably make sense to "derive" the write interface from the read interface.
stakx
@stakx: that is what i meant, it depends on the domain.
Henk Holterman
+1  A: 

First let me admit that I am making some assumptions about what you might intend doing. If this misses the point then let me know.

I am not sure how usefull the classes would be in either of your two options. I assume you would have calling code that would use an instance of a readonly repository and at othertimes an instance of a read/write repository, but the interfaces do not match so you would have to differenciate in your code anyway?

It might be better to provide a common interface and then throw exceptions if you try to write to the repository when it is readony and have your code handle the exceptions.

Chris Taylor
+1, agreed, there's no value-add here. Just a maintenance and learning headache for the class user. Compare with the FileStream class.
Hans Passant