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

使用泛型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

分享到:
评论

相关推荐

    Go-Grizzly允许您在GO中使用集合而不用泛型

    Grizzly通过使用接口和反射机制来实现类似泛型的功能,它定义了一系列的集合接口,如`Collection`、`List`、`Map`,这些接口提供了丰富的操作方法。 1. **Map**: Grizzly的`Map`接口模仿了Java中的Map,允许用户...

    Java集合、泛型和枚举

    泛型是Java SE 5.0引入的一个重要特性,它允许在类、接口和方法中使用类型参数,以提高代码的类型安全性和重用性。使用泛型可以确保集合中的元素类型一致,避免了类型转换的麻烦和潜在的ClassCastException。例如,`...

    java collection framework

    - **泛型类型**:自 Java 5 起,集合类支持泛型,可以在编译阶段检查类型安全。 - **性能考量**:根据实际需求选择合适的集合类,例如,如果需要频繁地添加或删除元素,则应选择 `LinkedList`;如果需要快速访问,则...

    C#集合容器(collection)详解

    - 性能略低于专门针对某种数据类型的泛型集合。 ##### 使用示例: ```csharp using System; using System.Collections; class Program { static void Main(string[] args) { ArrayList al = new ArrayList(); ...

    Java集合框架和泛型机制

     JDK API中专门设计用来存储其他对象的类,一般称为对象容器类,简称容器类,这组类和接口的设计结构也被统称为集合框架(Collection Framework)。集合框架中容器类的关系如下图示  主要从两方面来选择容器...

    java-collection-all-in-one.pdf

    它支持协变、逆变等高级特性,但在使用中需要注意类型擦除带来的影响,并通过通配符、类型边界等方法解决泛型在使用中可能遇到的问题。 类型擦除(Type Erasure)是Java泛型的一种机制,它在运行时移除泛型信息,以...

    关于java类集和的讲解

    从 JDK 1.5 开始,可以使用泛型来指定集合元素的类型,如 `List&lt;String&gt;`,这样可以提高代码的类型安全性。 总结来说,Java 类集合框架提供了一套丰富的接口和类,满足了不同数据结构和算法需求。理解并熟练使用...

    Introducing Visual Basic 2005 for Developers的第2章

    这段代码定义了一个Employee类,并使用泛型创建了一个专门用于存储Employee对象的集合。 #### 三、My命名空间 **概述** My命名空间是Visual Basic 2005的一个重要特性,它提供了一系列便捷的方法和属性,使得...

    集合问题小结

    - 在需要比较两个Student对象的场景中,可以使用匿名内部类来实现MyComparator接口,从而避免创建大量专门的比较类。 **三、集合** 1. **集合的必要性** - 集合提供了一种灵活的方式来存储和操作对象,弥补了...

    Java编程思想笔记(全)

    本章讲解了如何定义泛型类和泛型方法、如何使用泛型参数以及泛型通配符的使用场景。泛型可以提高代码的灵活性和可读性。 #### 第 16 章 数组 第十六章讲述了Java中的数组。数组是一种存储相同类型元素的集合。本章...

    java教程 第十课 集合

    `Arrays`类则专门针对数组提供了类似的功能,如排序、填充和转换为列表。 #### 异常处理 在操作集合时,可能会遇到多种异常,包括但不限于`NullPointerException`、`ClassCastException`、`...

    typscript-collection-sorter

    1. **泛型**:深入理解 TypeScript 泛型的概念,如何声明和使用泛型函数或泛型类,以及如何利用泛型约束确保类型安全。 2. **自定义比较函数**:在排序中,可能需要编写自定义的比较函数以根据特定的规则对元素进行...

    OCAOCP Java SE 7 Programmer I & II Study Guide (Exams 1Z0-803 & 1Z0-804)

    2. **泛型**:泛型的作用与优势,如何使用泛型类、泛型方法和泛型接口。 3. **注解**:注解的用途,自定义注解的方法。 4. **反射**:反射的基本概念及应用场景。 ### 五、备考策略 1. **考试概述**:对考试 1Z0-...

    .net Framwork 复习试题与笔记

    - **概念**:泛型允许开发者在类、接口或方法中使用类型参数。这种机制提高了代码的复用性和类型安全性。 - **好处**: - **可重用性**:同一个泛型类或方法可以应用于多种不同的数据类型。 - **类型安全**:避免...

    数据结构与算法:语言描述(中英文)

    示范说明了Collection类。本章还介绍泛型编程的概念。泛型编程允许程序员编写一个类或一种方法,并且把它用于众多数据类型。泛型编程是C#语言一种重要的新特性(在C#2.0以及更高版本中可用)。这种特性是如此重要以...

Global site tag (gtag.js) - Google Analytics