仅出于更好的可读性而将集合包装在一个简单的类中是否为时过早?


15

我有以下地图:

Map<Double, List<SoundEvent>> soundEventCells = new HashMap<Double, List<SoundEvent>>();

HashMap会将double值(它们是时间点)映射到相应的SoundEvent“单元格”:每个“单元格”可以包含多个SoundEvent。这就是为什么将其实现为的原因List<SoundEvent>,因为这正是它的本质。

为了提高代码的可读性,我考虑过实现一个非常简单的静态内部类,如下所示:

private static class SoundEventCell {
    private List<SoundEvent> soundEvents = new ArrayList<SoundEvent>();
    public void addEvent(SoundEvent event){
        soundEvents.add(event);
    }
    public int getSize(){
        return soundEvents.size();
    }
    public SoundEvent getEvent(int index){
        return soundEvents.get(index);
    }
    // .. remove() method unneeded
}

并且比地图声明(以及许多其他代码)看起来更好,例如:

Map<Double, SoundEventCell> soundEventCells = new HashMap<Double, SoundEventCell>();

这是过度杀伤力吗?您会在项目中这样做吗?


有人可能会争论说,从概念上讲,这已在《如何知道您是否编写了可读且易于维护的代码》中得到解决?如果您的同伴一直抱怨您的做事方式,无论是一种方式还是另一种方式,您最好进行改变以使他们感觉更好
gnat

1
是什么使声音事件列表成为“单元”而不是列表?单词的这种选择是否意味着一个单元格具有或最终将具有与列表不同的行为?
x代码

@DocBrown为什么?该类是private static因为它将仅由外部类使用,但与外部类的任何特定实例无关。那不是正确的用法private static吗?
阿维夫·科恩

2
@Doc Brown,Aviv Cohn:没有指定任何语言的标签,因此任何东西都可以同时是非。
Emilio Garavaglia 2014年

@EmilioGaravaglia:Java(我认为这很清楚,因为从语法上判断它可以是Java或C#,并且使用的约定将其范围缩小到Java;))。
阿维夫·科恩

Answers:


12

一点也不算过分。从所需的操作开始,而不是从“我可以使用HashMap”开始。有时,HashMap正是您所需要的。
就您而言,我怀疑并非如此。您可能想做的是这样的:

public class EventsByTime {
    public EventsByTime addEvent(double time, SoundEvent e);
    public List<SoundEvent> getEvents(double time);
    // ... more methods specific to your use ...
}

您绝对不希望有一堆这样的代码:

List<SoundEvent> events = eventMap.get(time);
if (events == null) {
   events = new ArrayList<SoundEvent>();
   eventMap.put(time, events);
}

或者,您可以只使用一种Guava Multimap实现。


因此,您主张使用本质上是一种信息隐藏机制的类作为一种信息隐藏机制?惊恐的事件。
罗伯特·哈维

1
实际上,是的,我确实有一个TimeLine专门用于此类事情的类:)这是一个薄薄的包装纸HashMap<Double, SoundEventCell>(最终,我确实采用了SoundEventCell代替List<SoundEvent>想法的包装)。所以我可以做,timeline.addEvent(4.5, new SoundEvent(..))并封装下层内容:)
Aviv Cohn 2014年

14

虽然它在某些方面可能有助于可读性,但也可能使事情复杂化。为了流利,我个人不希望包装或扩展收藏,因为新包装在最初阅读时对我意味着可能需要注意一些行为。认为它是“最少惊讶原则”的阴影。

坚持使用接口实现意味着我只需要担心接口。当然,具体的实现可能包含其他行为,但是我不必担心。因此,当我尝试通过某人的代码找到自己的方式时,我更喜欢普通的界面以提高可读性。

另一方面,如果您发现确实可以从增加的行为受益的用例,可以通过创建完整的类来改进代码。


11
包装器还可以用于删除(或隐藏)不必要的行为。
Roman Reiner 2014年

4
@RomanReiner-我谨防此类情况。今天不必要的行为经常是程序员明天咒骂您的名字。每个人都知道List罐头可以做什么,并且它有充分的理由完成所有这些事情。
Telastyn 2014年

我很欣赏维护功能的愿望,尽管我认为解决方案是功能和抽象之间的谨慎平衡。 SoundEventCell可以IterableSoundEvents 实现,它将提供soundEventsmember 的迭代器,因此您可以读取(但不能写入)任何列表。我犹豫要掩盖复杂性,就像List在将来可能需要更动态的东西时犹豫不决一样。
尼尔

2

包装它会将您的功能限制为仅决定决定编写的那些方法,基本上没有任何好处地增加了代码。至少,我会尝试以下方法:

private static class SoundEventCell : List<SoundEvent>
{
}

您仍然可以从示例中编写代码。

Map<Double, SoundEventCell> soundEventCells = new HashMap<Double, SoundEventCell>();

就是说,只有在列表本身需要某些功能时,我才这样做。但是我认为您的方法对此会显得过大。除非您有理由要限制对大多数List方法的访问。


-1

另一个解决方案可能是使用公开列表的单个方法定义包装器类:

private static class SoundEventCell
{
    private List<SoundEvent> events;

    public SoundEventCell(List<SoundEvent> events)
    {
        this.events = events;
    }

    public List<SoundEvent> getEvents()
    {
        return events;
    }
}

这使您可以用最少的代码来命名类,但是仍然可以进行封装,例如,使类不可变(通过在构造函数中进行防御性复制并Collections.unmodifiableList在访问器中使用)。

(但是,如果这些列表确实仅在此类中使用,我认为您最好Map<Double, List<SoundEvent>>Multimap<Double, SoundEvent>docs)替换您的列表,因为这样通常可以节省很多空检查逻辑和错误。)

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.