I have a simple class below, it has a property Pages
of type IList.
There are some options to implement this property, it can be an Array
or a Collection
/ List
/ even ReadOnlyCollection
public class Book
{
private string[] _pages;
public Book(string[] pages)
{
_pages = pages;
}
public IList<string> Pages
{
get
{
return _pages;
//return new Collection<string>(_pages);
//return new List<string>(_pages);
//return new ReadOnlyCollection<string>(_pages);
}
}
}
At design time , I do not know which actions that its clients will use the property but choosing any option above will affect its clients.
If a client uses Book
class as below
var book = new Book(new[] {"A", "B"});
var pages = book.Pages;
pages[0] = "A2";
Not all implementation options of the property Pages
will work for the client.
Option 1: returning an Array for Pages // OK, it works
public IList<string> Pages
{
get
{
return _pages;
}
}
Option 2: returning a Collection for Pages // KO, it throws an exception NotSupportedException Collection is read-only
public IList<string> Pages
{
get
{
return new Collection<string>(_pages);
}
}
Option 3: returning a List for Pages // OK, it works
public IList<string> Pages
{
get
{
return new List<string>(_pages);
}
}
Option 4: returning a ReadOnlyCollection for Pages // KO, it throws an exception SystemException Collection is read-only
public IList<string> Pages
{
get
{
return new ReadOnlyCollection<string>(_pages);
}
}
I don't think it could be wrong on the client side. Could anyone please give some explanation and suggest a good type for the Page
?
Another option would be to define your own collection type PageCollection
for example and use it in your Book
class. The advantage of this is, that you hide the detail of how the collection is implemented. Another advantage of this approach is, that you can provide "special" methods for the collection.
For example you could extend an existing collection (it may also be a good advise to create a custom class for Page
, instead of using plain strings:
public class PageCollection : List<Page>
{
// additional methods
}
You can also wrap an existing collection. This has the advantage that you have full controll over the methods you want to provide to the "user".
public class PageCollection : IEnumerable<Page>
{
private List<Page> _innerCollection = new List<Page>();
public void RipOut(IEnumerable<Page> pages)
{
foreach (var page in pages)
{
_innerCollection.Remove(page);
}
}
// other methods
public IEnumerator<Page> GetEnumerator()
{
return _innerCollection.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
}
If I understand your question, you are asking: How do I design a class without knowing how it will be used?
The answer is, design it by putting in the functionality you want to support.
In your example: You have a book with pages. Would you like the user to be able to go to page 0, rip it out, and replace it with something else? If so, make it a List. If not, make it a ReadOnlyCollection.
I would recommend exposing the property as what it actually is (List or ReadOnlyCollection), that way you give clear intent into what is allowed and don't have the client guessing if they can or cannot replace the page.
So finally I come with this design. The custom PageCollection
has Add/Remove methods will allow me to add validation behavior inside. If using the List<Page>
, this is not possible.
The Book now exposes IPageCollection
Pages which has less methods than the IList.
Updated: Below are reasons to create the new interface:
.
public class Page
{
}
public interface IPageCollection : IReadOnlyList<Page>
{
void Add(Page page);
void Remove(Page page);
}
public class PageCollection : IPageCollection
{
public PageCollection(IList<Page> pages)
: base(pages)
{
}
public void Add(Page page)
{
}
public void Remove(Page page)
{
}
...
}
public class Book
{
private readonly PageCollection _pages;
public Book(IList<Page> pages)
{
_pages = new PageCollection(pages);
}
public IPageCollection Pages
{
get { return _pages; }
}
}
(thanks for suggestions from guys John, Thomas...)
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.