从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;
}
}
答案 0 :(得分:5)
潜在的问题有点复杂:
在设计软件时,您需要将所有这些要点放在脑海中。随着时间的推移,您将会感受到您所犯的错误以及如何避免错误。计算机像转储一样缓慢而且速度很慢,但从来没有一个完美的解决方案。你可以努力使它在你写作时尽可能好。
如果您想要进行防御性编码,则应始终复制您获得或公开的任何数据。当然,如果“数据”是您的整个数据模型,那么每次调用方法时都无法复制所有内容。
解决这个僵局的问题:
尽可能多地使用不可变量。创建了不可变项和值对象,之后永远不会更改。除非创建非常昂贵,否则它们总是安全且性能良好。懒惰的创造在这里会有所帮助,但这通常是它自己的蠕虫。 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);
}