Which is the better C# class design for dealing with read+write versus readonly
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 :)
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.
(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:
The read-only class is "heavier" than it needs to be.
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.
I would say design #2, but then you ought to change the name of the ReadOnlyRepository to something like ReadRepository.
Inheritance defines an IS-A relation between the classes, ans saying 'a ReadWriteRepository is a ReadOnlyRepository' doesn't sound logical. But 'ReadWriteRepository is a ReadingRepository' does.
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.
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.
I would suggest having a base class ReadableFoo, with a sealed derived class ImmutableFoo whose constructor takes a ReadableFoo, and a possibly-inheritable derived class MutableFoo, and possibly a class ReadableShadowFoo whose constructor would take a ReadableFoo (which may or may not be mutable), but which would act as a read-only wrapper to it.
The answer by Eric means SOLID Principle ISP. It´s so easy and basic to use.
精彩评论