2014-10-21 2 views
1

Часто в java мне нужно получить значение свойства объекта, который находится глубоко в этом объекте. Например, если я уверен, что все мои суб-объекты не нулевой, я могу сделать это:Лучшая практика: получить глубокое значение объекта

public function getDeepValue(A a) { 

    String value = a.getB().getC().getListeD().get(0).getE().getValue(); 
    return value; 
} 

Но в случае суб объектов родителя может быть пустым, я должен проверить каждый объект.
Чтобы сделать это, я вижу 2/3 решения:

Первый шаг за шагом:

public function getDeepValue(A a) { 

    if(a == null){ 
     return null; 
    } 

    B b = a.getB(); 
    if(b == null) { 
     return null; 
    } 

    C c = b.getC(); 
    if(c == null){ 
     return null; 
    } 

    List<D> ds = c.getListeD(); 
    if(ds == null || ds.size() == 0){ 
     return null; 
    } 

    D d = ds.get(0); 
    if(d == null) { 
     return null; 
    } 

    E e = d.getE() 
    if(e == null){ 
     return null; 
    } 

    return e.getValue(); 
} 

Во-вторых, тест все в одном, если блок, тааак загрязнен:

public function getDeepValue(A a) { 

    if(a != null && a.getB() != null && a.getB().getC() != null && a.getB().getC().getListeD() != null && a.getB().getC().getListeD().size() > 0 && a.getB().getC().getListeD().get(0) != null && a.getB().getC().getListeD().get(0).getE() != null){ 
     return a.getB().getC().getListeD().get(0).getE().getValue(); 
    } 

    return null; 
} 

Третий раствор, используя блок захвата try:

public function getDeepValue(A a) { 

    try { 
     return a.getB().getC().getListeD().get(0).getE().getValue(); 

    } catch(NullPointerException e) { 
     return null; 

    } catch(IndexOutOfBoundsException e) { 
     return null; 
    } 
} 

Решение 1 кажется n ot слишком плохо, но требует много кода. Это, как правило, решение, которое я использую.
Решение 2 для меня действительно грязное ...
В документе я действительно как решение 3, но это хорошее решение с точки зрения выступлений?

Есть ли другие приемлемые решения?
Спасибо за помощь, я надеюсь, что мой английский не так уж плохо ..
Привет

+5

Релевантно читать: «закон деметры» (например, этот: http://www.ccs.neu.edu/research/demeter/demeter-method/LawOfDemeter/paper-boy/demeter.pdf) – reto

+1

Лучшая практика было бы рефакторированием, поэтому вам не нужно проходить через большую цепочку возможных значений null. Из трех представленных решений мне нравится, по крайней мере, третий. Вы заметите, что он также ловит исключения, которые могут быть получены из методов get и преобразуют их в нулевое возвращаемое значение. –

ответ

-1

с точки зрения решения производительности 3 является лучшим вариантом. Кроме того, он аккуратно и легко понять, например, глядя на примере петли:

int[] b = somevalue; 
for(int i=0;i<b.length;i++){ 
    //do something 
} 

в этом случае для каждой итерации мы выполняем условие. Тем не менее, существует и другой подход к нему, который использует попытаться поймать

int[] b = somevalue; 
try{ 
    for(int i=0;;i++){ 
    //do something 
    } 
}catch(IndexOutOfBoundException e){ 
    // do something 
} 

на втором растворе, цикл продолжает идти, пока мы не достигнем конца цикла, который затем бросает IndexOutOfBoundException как только мы достигаем конца массив. что означает, что мы больше не проверяем состояние. таким образом, быстрее.

1

Решение №3 выглядит просто, но это потенциально может скрыть целый ряд проблем. Это может быть адекватным решением, если у вас есть полный доступ ко всем классам в цепочке, и вы знаете, что происходит в каждом методе, и вы можете гарантировать, что эти методы не вызовут проблем с вашим try/catch, и вы никогда не изменит их ... это много условий, чтобы сделать это стоящим решением, но я могу себе представить, что это возможно полезно достаточно.

Решение №2 выглядит ужасно для меня, особенно если один или несколько методов get являются узким местом (например, медленным запросом базы данных или использованием блокирующего сетевого соединения). Чем раньше в цепочке такое потенциальное узкое место, тем хуже будет потенциально, поскольку вы вызываете его снова и снова. Это, конечно, зависит от реализации рассматриваемых методов (даже если один из них медленный, например, может быть кэширован результат), но вы не должны нуждаться в, чтобы знать это в своем клиентском коде. Даже с эффективными или тривиальными реализациями, у вас все еще есть накладные расходы на повторные вызовы методов, которые вам не нужны.

Решение № 1 является лучшим из трех, но это, вероятно, не самый лучший возможно. Это решение занимает больше строк кода, чем два других, но оно не повторяется, и оно не будет устранено реализацией других методов. (Примечание. Если у вас нет доступа к классам в цепочке для рефакторинга, я бы использовал это решение.)

Лучшим решением, чем # 1, было бы рефакторинг классов, чтобы код клиента не нуждался знать об этой цепочке вообще. Что-то вдоль этих линий:

class Client { 
    public Mumble getDeepValue(A a) { return a == null ? null : a.getDeepValue(); } 
} 

class A { 
    private B b; 
    public Mumble getDeepValue() { return b == null ? null : b.getDeepValue(); } 
} 

class B { 
    private C c; 
    public Mumble getDeepValue() { return c == null ? null : c.getDeepValue(); } 
} 

class C { 
    private List<D> ds; 
    public Mumble getDeepValue() { 
     D d = ds == null || ds.size() == 0 ? null : ds.get(0); 
     return d == null ? null : d.getDeepValue(); 
    } 
} 

class D { 
    private E e; 
    public Mumble getDeepValue() { return e == null ? null : e.getMumble(); } 
} 

class E { 
    private Mumble m; 
    public Mumble getMumble() { return m; } 
} 

Как вы можете видеть, самая длинная цепь любые из этих классов имеют, чтобы получить доступ к открытым членам элемента коллекции, который является частным членом класса. (По существу ds.get(0).getDeepValue()) Клиентский код не знает, насколько глубока кроличья дыра, только то, что A предоставляет метод, который возвращает Mumble. Client даже не нужно знать, что классы B, C, D, E, или List существуют в любом месте!

Кроме того, если бы я разрабатывал эту систему с нуля, я бы неплохо осмотрел, можно ли ее перестроить так, чтобы фактический объект Mumble был не таким глубоким. Если бы я мог разумно уйти с сохранением Mumble в пределах A или B, я бы рекомендовал это сделать. В зависимости от приложения это может быть невозможно.

+0

Также это отделяет источник и вызывающего абонента, поэтому, если глубокое значение перемещается в другое место, для него потребуется небольшое количество изменений, которые необходимо внести в исходный код. – ArtB

Смежные вопросы