为类成员提供对另一个类成员的引用

时间:2012-01-06 09:59:54

标签: java oop

从1到10的等级,从安全编程实践的角度来看,下面有多糟糕?如果你发现它比五更糟糕,你会做什么呢?

我的目标是将B中的地图列表中的数据转换为A.在这种情况下,如果它是数据的副本或对原始数据的引用,则可以。我发现这种方法最快,但我对此感到不安。

public class A {
    private List<Map<String, String>> _list = null;
    public A(B b) {
        _list = b.getList();
    }
}

public class B {
    private List<Map<String, String>> _list = new ArrayList<Map<String, String>>();
    public List<Map<String, String>> getList() { 
        // Put some data in _list just for the sake of this example...
        _list.add(new HashMap<String, String>());
        return _list; 
    }
}

4 个答案:

答案 0 :(得分:5)

潜在的问题有点复杂:

  • 从安全角度来看,这非常非常糟糕。
  • 从表现的角度来看,这非常非常好。
  • 从测试的角度来看,这很好,因为课堂上没有任何内容可以让你从测试中轻易获得
  • 从封装的角度来看,由于您暴露了类的内部状态,因此它很糟糕。
  • 从编码安全的角度来看,这很糟糕,因为有人最终会滥用这一点来解决一些“整齐”的技巧,这会在其他地方造成奇怪的错误,你会浪费大量的时间来调试它。
  • 从API的角度来看,它可以是:很难想象一个API更简单但同时,它不会传达你的意图,如果你需要更改基础数据,事情就会严重破坏结构

在设计软件时,您需要将所有这些要点放在脑海中。随着时间的推移,您将会感受到您所犯的错误以及如何避免错误。计算机像转储一样缓慢而且速度很慢,但从来没有一个完美的解决方案。你可以努力使它在你写作时尽可能好。

如果您想要进行防御性编码,则应始终复制您获得或公开的任何数据。当然,如果“数据”是您的整个数据模型,那么每次调用方法时都无法复制所有内容。

解决这个僵局的问题:

  • 尽可能多地使用不可变量。创建了不可变项和值对象,之后永远不会更改。除非创建非常昂贵,否则它们总是安全且性能良好。懒惰的创造在这里会有所帮助,但这通常是它自己的蠕虫。 Guava提供了一套全面的系列,在创建后无法更改。

    不要过分依赖Collections.unmodifiable*因为支持集合仍然可以改变。

  • 使用copy-on-write数据结构。如果A或B开始更改它,基础列表将自行克隆,上述问题就会消失。这将使每个自己的副本有效地将它们彼此隔离。不幸的是,Java没有对这些内置的支持。

答案 1 :(得分:2)

  

在这种情况下,对我来说,如果它是数据的副本或对原始数据的引用就没问题。

这是关键点。

传递对象实例的速度最快,但允许调用者更改它,并且还可以使以后的更改可见(没有快照)。

通常,这不是问题,因为调用者不是恶意的(但您可能希望防止编码错误)。

如果您不希望调用者进行更改,可以将其包装到immutable wrapper

如果您需要快照,可以克隆列表。

无论哪种方式,这只会快照/保护列表本身,而不是其单个元素。如果这些是可变的,同样的推理再次适用。

答案 2 :(得分:2)

我想你会在效率和封装之间做出选择。通过直接访问类的成员,它将改变其状态。这可能是出乎意料的,并导致令人讨厌的惊喜。我还要说它增加了两个类之间的耦合。

另一种方法是让信息专家原则决定并将工作留给有信息的班级。你必须判断那些假定用A级做的工作是否真的是B级的责任。

但实际上,速度和清洁代码可能是相互冲突的利益。有时候你只需玩脏就能让它足够快。

答案 3 :(得分:1)

您创建的所有内容都是对B._list的引用。所以10如果你想复制这些项目。 您可以迭代所有b._list项并手动将它们添加到A._list:

public A(B b) {
    _list = new List<Map<String, String>> ();
    for (Map<String,String> map : b.getList()) {
        Map<String,String> newMap = new HashMap<String,String>();
        while(map.keySet().iterator().hasNext()) {
            String key = map.keySet().iterator().next();
            newMap.put(key,map.get(key));
        }
        _list.add(newMap);
}