`
experience
  • 浏览: 193916 次
社区版块
存档分类
最新评论

使用泛型collection还是专门类

阅读更多

Dale Emery:

I'm puzzling about the wisdom of using generics, especially early in the
development of a product.
Here's an example. Suppose I'm building a book catalog (as part of a larger
system). Almost immediately I want some kind of book list. In C#, I could
jump right to List<Book>. But if I do that, I automatically expose a
zillion methods that I may or may not want, and that may or may not work as
intended if called. It seems to me that if I expose List<Book> throughout
my product, I lose some control of the list. I don't know whether that's a
problem, but my gut tells me it is.
So instead I write my own BookList class. The BookList class, of course,
exposes only the few methods that my book catalog needs right now: Add(),
Delete(), Contains(), a few others. But after about the third test I find
that the easiest thing to do is add a List<Book> field and delegate
everything to it. So my BookList class is essentially a simplifying wrapper
around List<Book>.
Later I may add some features that List<Book> doesn't handle. But for now,
delegating is the easiest thing to do.
So that's how I do it. Something about this bugs me, though I can't put my
finger on just what.
What are your thoughts on this? Do you prefer to just use List<Book>, and
ignore the interface bloat? Do you write BookList from scratch and not
delegate to List<Book>? Do you do as I do and delegate? Or something else?
And above all: What benefits do you see to your preferred style? What
drawbacks to the other styles?

=============================================

Ron Jeffries:

I used to struggle with this in Smalltalk. My default practice then
was to build my own collection class, so that I could give it
custom methods. But often, like you, I found myself wanting more and
more collection behavior, so that I'd have been mostly better off
just using a standard collection.
In Java / C#, I usually go directly to things like List<Book>, which
gives me a little type assistance, and the flexibility to go ahead
fairly smoothly.
Regarding "type assistance", I am not referring to gaining an
advantage from the strict typing. I hate that. But given that Java
/ C# have strict typing, using List<Book> at least lets me avoid
most of the gratuitous (Book) casts that I'd otherwise have to
write.
However, what I'm liking most, when it happens, is when I have an
abstraction that isn't mostly collection, but is mostly something
else. In the reporting part of the shotgun app, we have Report,
which is of course a collection of Pages, and Page, which is a
collection of Paragraph (so far). These entities, though they do not
have much behavior, seem clearly to represent something that's
actually going on.
What this makes me want to do is ask what this List<Book> really is.
Is it a Library? Is it some kind of Recommendation? What's really
going on, I'll ask myself. Then I'll code that.
One more thing. As a rule, I think that collections faintly smell of
violation of the Law of Demeter. We get things out of collections,
and then do things to them, and then (often) put them back. So I
suspect, when I find myself looking for a List, that I'm quite
possibly looking in the wrong direction. Another clue that there may
be an abstraction missing.
Just me, just my thoughts ...
Ron Jeffries
www.XProgramming.com
It's easy to have a complicated idea.
It's very very hard to have a simple idea.
-- Carver Mead

=======================================

Dale:

This is a catalog of books, annotated with quotations and comments. I want
the basic bibliographic features of EndNote, combined with the terrific
note-taking features of EverNote. Some features I'd like:
- Quote specific passages (writing the quotation into the software), citing
page numbers.
- Annotate each book with any number of comments
- Associate each comment with the chunk of text (book, chapter, titled
section, page(s), quotations).
- Assign any number of topics to a book.
- Assign any number of topics to a comment.
- Distinguish my comments from quotations (my thoughts from the authors'
thoughts).
- Produce bibliographies in a variety of bibliographic styles.
- Produce a bibliography on a given topic, annotated with the book-level
comments I've entered for that topic.
- Search and sort books by title, author/editor, or other fields.
- Search books, quotations, and comments by topic.
- Search quotations and comments by content.
- Produce a list of comments I've made about a topic, with citations to the
chunk of text that inspired each.
- Maybe similar for journal and magazine articles, web sites, and other
text-based media.
Does that give you any ideas about what this List<Book> really is?

===========================================

George:

My own feeling is that generic collections don't communicate intent very
well. I see the same thing in over-reliance on primitives. When all
the data is of type String, the code starts getting very messy and
procedural. I think the same thing happens when the domain object is
just a collection, and the code to do something with that collection
resides wherever the results of that "doing something" is needed, rather
than encapsulated with the data.

=================================================

jonathan:

Think in terms of how many classes will be dependant on BookList. If
it's a highly visible class, used throughout your app, you want an
interface so you can avoid refactoring when you eventually add the
findByAuthor() method.
On the other hand there's the YAGNI principle. If only a few classes
interact with the BookList, then it would be better to wait until you
know you need the added complexity and refactor at that point.

===========================================

Jeff:

Greetings Dale,
I would tend to keep the wrapper class and name it BookCatalog. It's a
non-TDD reason, but either List<Book> or BookList would expose
implementation detail unnecessarily.
Some TDD/simple design reasons. I'm not sure any of these are very strong.
- using a List implies something about the catalog that you possibly
don't want: the assumption that it's ordered. So I think that'd be
violating expressiveness, and potentially YAGNI (of course, using
Collection or whatever C# analog would fix this problem)
- requiring clients to specify both type (Book) and collection
implementation (List) information in more than one place possibly
violates duplication. (Maybe the need to continually express type
information itself is redundant...)
- I think it's just easier, from a TDD perspective, to use the tests to
start describing things that clients can do with the collection. I'm
willing to bet that there will be some things you end up not wanting
clients to do with the collection; you could of course just wait and see.
I would rather build up abstractions like BookCatalog from scratch,
rather than try to control overblown implementations like List<Book>.
Jeff

===================================================

Dale:

Hi Jeff,
> I would tend to keep the wrapper class and name it BookCatalog. It's a
> non-TDD reason, but either List<Book> or BookList would expose
> implementation detail unnecessarily.
I think that is indeed a TDD reason. Here's my thinking: The name
BookCatalog refers to a domain concept. The methods I put in the class
represent domain concepts. List<Book> includes methods that are not in my
domain, which injects noise into the communication. Also BookCatalog
describes more fully the domain concept I'm representing. Catalog suggests
searching and sorting better than does List.
So I'm applying this principle (related to the ones I described a few
messages ago): To refer to domain concepts in code, use names and coding
constructs that inject the least technical noise.
Dale

========================================================

Charlie:

That's what I thought/hoped you meant. It seemed that some of the
thread was focusing (too much IMO) on the pros and cons of generics
when the issue is exactly the same if we expose, for example, an
ArrayList.
My practice on this is to expose a collection to any object that
is going to use it purely as a collection. Most commonly, that's
the domain object that has the collection in it.
Sometimes, it's useful for me to expose the collection as a property
of the domain object, in order to avoid a lot of pass-through methods
and to create a syntax that makes sense to me. So, I might do
something like...
BookCatalog.Books.Add( myBook );
I find this quite expressive. It says that a BookCatalog /has/
a list of Books and that it (probably) has other methods and
properties of it's own.
While this does expose the list more than using a passthru method,
I can't see it as a universally bad thing. Of course, the exposed
property Books, should normally not be of the implementation class,
but should be an interface type like ICollection<Book>.
When to introduce this is another question. If I start with nothing
but a collection, I'll probably use it directly at first and create
the domain class as soon as I need a property or method on it.
Charlie

======================================================

Dale:

Hi Charlie,
> That's what I thought/hoped you meant. It seemed that some of the
> thread was focusing (too much IMO) on the pros and cons of generics
> when the issue is exactly the same if we expose, for example, an
> ArrayList.
I think we can attribute most of that to my featuring that red herring in my
initial problem statement.
In retrospect, I can see that my puzzle wasn't just about generics, but
about prepackaged classes in general. And not just about using them, but
about exposing them. And not just about exposing them, but about exposing
them to clients of essential domain entities. And not just about exposing
them, but about defining essential domain entities in terms of
implementation technology.
If I were to restate the problem now, it seems kinda silly: I'm puzzling
about the wisdom of defining essential domain entities in terms of
implementation technology, and of exposing that underlying technology to
clients of essential domain entities.
If I state the problem that way, the solution is a laughably obvious.
Dale

========================================================

 George:

Manuel,
Someplace lost in translation the class BookList got renamed to
BookCatalog but I believe they are the same class just different names
for the same thing. So assuming this then ...
What happens if you want to search by Publisher and Author, by only
Publisher, or only by Author? Do you code this?
public class BookCatalog {
List<Book> findBooksByAuthor( ... ) {..}
List<Book> findBooksByPublisher( ... ) {..}
List<Book> findBooksByAuthorAndPublisher( ... ) {..}
Or are you suggesting you only implement the first two methods, then
make it the responsibility of the object that calls the method to
search the other criteria.
And what happens if you want to add searches by publish date between
two days, publishing country, etc. The number of combinations for
parameters become endless, unless you are proposing that the object
making the call is responsible for subdividing the BookList to include
additional criteria.
A better way might be to code this:
public class BookCatalog {
BookCatalog findByAuthor( ... ) {..}
BookCatalog findByPublisher( ... ) {..}
BookCatalog findByPublishDates( DateTime,DateTime ) {..}
BookCatalog findByPublishingCountry( ... ) {..}
Now everything about the BookCatalog stays within the catalog and you
continue to enjoy new features added to it over time.
I'd also implement BookCatalog with an IBookCatalog interface as well
to provide other benefits. As well, I'd making it possible to
enumerate it by implementing IEnumerator<BookCatalog> and
IEnumerable<BookCatalog>. This way I am free to choose whatever
method is appropriate to store and enumerate the list of books;
whether it is a List<Book>, SortedList<key, Book>, Dictionary<key,Book>.
If I added a lot of criteria, I'd change the find logic to accepting
some kind of criteria object, such as:
public class BookCatalog {
public BookCatalog find( List<IBookFindCriteria> ... );
And so on...
Regards,
- George

分享到:
评论

相关推荐

    泛型和泛型集合类用法

    泛型和泛型集合类用法泛型和泛型集合类用法泛型和泛型集合类用法泛型和泛型集合类用法泛型和泛型集合类用法

    【Flutter】Dart 泛型 ( 泛型类 泛型方法 特定类型约束的泛型 ).zip

    【Flutter】Dart 泛型 ( 泛型类 | 泛型方法 | 特定类型约束的泛型 ) https://hanshuliang.blog.csdn.net/article/details/114059611 博客源码快照

    C#泛型类、泛型方法、泛型接口、泛型委托的实例

    泛型类、泛型方法、泛型接口、泛型委托 泛型类、泛型方法、泛型接口、泛型委托

    泛型工具类

    泛型工具类,用于dao的超类,获取实体类的类型。

    c#使用 和 继承 泛型类

    c#使用 和 继承 泛型类

    利用反射生成泛型类对象

    对于框架设计者来说,便捷的代码,是很重要的一部分。 反射和泛型是一种重要的解决途径。 此代码是一个生成泛型对象的类。...希望能帮助那些为查找泛型构造器、非静态内部泛型类的对象生成而烦恼的童鞋。

    WCF中使用泛型效果示例

    WCF服务的操作方法中实现T GetEntity(int id) where T: EntityBase

    java 继承非泛型类示例

    java 继承非泛型类示例 java 继承非泛型类示例 java 继承非泛型类示例

    java 继承泛型类示例

    java 继承泛型类示例 java 继承泛型类示例 java 继承泛型类示例

    电影售票系统使用泛型集合

    使用泛型集合维护有继承关系的电影票对象 座位数可以在系统中指定 点击座位可购买不同类型的电影票 使用简单工厂模式创建不同类型的电影票 使用多态实现不同类型电影票的价格计算 使用接口实现电影票的打印 使用序列...

    自己实现vector不使用泛型

    这是自己用在不使用泛型的基础上,用c++实现的一个vector功能,经过10万测试考验,没问题可用,如果大家有更好的建议和意见,还请指出,有不对的地方也请指出,大家一起进步,学习,加油。

    反射创建泛型类

    通过反射创建带有泛型参数的类 1.Limin.Reflector.DLL中的BaseDal.cs里包含要创建的带泛型参数的类 2.Limin.Reflector.Invoke中的Factory.cs完成泛型类的创建 代码写的不是很好,不足之处,请多多指教

    C#泛型集合使用实例

    C#泛型集合使用实例C#泛型集合使用实例C#泛型集合使用实例

    c# 泛型的使用,教你如何用泛型

    关于如何在C#中使用泛型,希望对大家有帮助

    java 泛型类的类型识别示例

    java 泛型类的类型识别示例 java 泛型类的类型识别示例 java 泛型类的类型识别示例

    c#泛型类、泛型方法、泛型接口、泛型委托

    c#泛型类、泛型方法、泛型接口、泛型委托

    C# 2.0中泛型编程思想分析

     本文讨论泛型使用的一般问题,比如为什么要使用泛型、泛型的编写方法、泛型中数据类型的约束、泛型中静态成员使用要注意的问题、泛型中方法重载的问、泛型方法等,通过这些使我们可以大致了解泛型并掌握泛型的一般...

    C#泛型集合与非泛型集合

    在.NET FCL为我们提供了很多...结论:如果在C#2.0版本以上,尽量使用泛型集合类,而不使用非泛型集合类。因为,1. 泛型编程是从c#2.0开始才被.net支持的。2.泛型集合在性能和类型安全方面优于非泛型集 合。 。。。。

    DropDownList无极分类 用泛型

    三层结构 DropDownList无极分类 用泛型 DropDownList无极分类 用泛型 DropDownList无极分类 用泛型 DropDownList无极分类 用泛型

    泛型dao 泛型dao 泛型dao

    在这里我建议项目最好使用一个共通的DAO,因为这样会为你省去非常多的类,而那些类里的逻辑往往差不多。当然是用共通的DAO你需要对结果转型,转成你需要的bean,但这也比写那么多DAO强多了,你可以放下包袱,只关注...

Global site tag (gtag.js) - Google Analytics