2015-12-10 3 views
1

У меня есть класс с двумя переменными экземпляра.Какой лучший дизайн?

class SomeName { 
    String a; 
    String b; 
    String c; 
    String d; 

    SomeName(String a, String c){ 
    this.a = a; 
    this.c = c; 
    process(a,b); 
    process(c,d); 
    } 

    public void process(String a, String b){ 
    // Some complex operation on 'a' 
    // instance variable 'b' is initialized based on the result 
    b = result; 
    } 

} 

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

process(a,b); 
process(c,d); 

Есть ли альтернативные способы достижения того же, что я пытаюсь сделать?

Другими словами ........

Я получаю значение «а» снаружи. Мне нужно сделать некоторую операцию над этим и назначить его «b». То же самое относится к 'c' и его паре 'd'. Поскольку обе операции одинаковы, я написал это как обычный метод process(). Но я передаю переменные экземпляра в качестве аргументов, которые являются плохим дизайном. Пожалуйста, предложите альтернативу для этого плохого подхода к дизайну.

+1

как заметка на полях, обратите внимание, что метод 'процесса (String а, строка б)' может получить перекрываться наследуемым классом. вы можете просто перегрузить метод 'process' с помощью частного, и вызвать это или сделать его окончательным. – SomeJavaGuy

+0

Коррекция: у вас есть класс с четырьмя переменными экземпляра. –

+2

вы не должны вызывать неконфиденциальные методы от конструкторов. –

ответ

3

Идея выполнения методов инициализации в конструкторе совершенно прекрасна.

Однако, ваш код не является хорошим, чтобы сделать это:

//THIS WILL BREAK 
public void process(String a, String b){ 
    //`b` is method local and Strings are immutable - you will change nothing with this! 
    b = result; 
} 

Вместо этого, вы должны кодировать как

SomeName(String a, String c){ 
    this.a = a; 
    this.c = c; 
    this b = process(a); 
    this.d = process(d); 
} 

с процессом как

private final String process(String x) { 
    //do your stuff 
    return result; 
} 

Делая окончательный (или частные или оба), вы убедитесь, что подклассы не возиться с ним.

0

Поддерживая c и d, вы явно устанавливая состояние к классу, который не очень хорошо.

Кроме того, в вашем конструкторе есть temporal coupling - сначала вы должны установить a, а затем вы должны обработать его, чтобы получить значение b. Вы также должны сначала установить c, а затем вы должны обработать c, чтобы получить значение d. Если вы забыли по какой-либо причине установить значение a, вы все равно сможете вызвать расчет b, что приведет к по меньшей мере NullPointerException. Лучше всего сохранить значение a и получитьb по запросу.

Также есть ли призывы к process действительно необходимы для построения надлежащего экземпляра типа SomeName? В соответствии с подписью конструктора, нет.

Ваш конструктор вводит два параметра, и это единственная необходимая вещь, необходимая для получения экземпляра класса. Это означает, что вы можете избавиться от вызовов process в конструкторе.

Когда вам действительно нужно получить значение b, тогда просто вызовите метод, называемый produceValueOfB() или что-то в этом роде. Этот метод будет использовать уже установленное значение a и вернет вам другое значение (так называемое b). Нет необходимости поддерживать состояние. То же самое касается другой переменной экземпляра.

Итак, ваш класс может выглядеть следующим образом:

class SomeName { 
    final String a; 
    final String c; 

    SomeName(String a, String c){ 
    this.a = a; 
    this.c = c; 
    } 

    public void getValueOfB() { 
    return getValueBasedOnInstanceVariable(a); 
    } 

    public void getValueOfD() { 
    return getValueBasedOnInstanceVariable(c); 
    } 

    private String getValueBasedOnInstanceVariable(String x) { 
     // Some complex operation on x 
     return result; 
    } 
} 

Имея это, вы класс:

  • без гражданства
  • легче испытываться
  • легче поругаем для тестирования другого классы, которые зависят от этого
  • очиститель
+0

'getValueOfB()' и 'getValueOfD()' имеют одинаковую логику с именем переменной только как разность. это хороший дизайн? –

+0

хорошо указал @sharonbn. Я тоже думаю то же самое. –

+0

Да, они могут совместно использовать некоторую общую логику, которая может быть извлечена другим способом ('private'), но в целом хорошо их имена, чтобы показать, что они на самом деле делают. Такие слова, как 'process',' save', 'submit' и т. Д.являются ласковыми словами, и для того, чтобы ваш (публичный) код был чистым, вы должны тщательно рассмотреть именование. :) –

-1

Я предлагаю использовать соглашение Java Bean для сеттеров и геттеров как способы получения/назначения значений для переменных экземпляра. Преимущество этого ПОДХОД является то, что вы можете присвоить значения для переменных экземпляра после застройщик закончил:

public class SomeName { 
    private String a; 
    private String b; 
    private String c; 
    private String d; 

    public String getA() { return a; } 
    public void setA(String newA) { a = newA; } 
    public String getB() { return b; } 
    public void setB(String newB) { b = newB; } 
    public String getC() { return c; } 
    public void setC(String newC) { c = newC; } 
    public String getD() { return d; } 
    public void setD(String newD) { d = newD; } 

    SomeName(String a, String c) { 
     setA(a); 
     setC(c); 
     setB(process(a)); 
     setD(process(c)); 
    } 

    public String process(String source) 
    { 
     String result = source; 
     // Some complex operation on 'source' 
     return result; 
    } 
} 

позже:

SomeName instance = new SomeName("a", "b"); 
// need to change instance's state 
String newA = ... ;// get from external source 
instance.setC(process(newA)); 

EDIT: если значение с должно измениться при значении изменения, вы можете отразить эту зависимость в сеттере следующим образом:

public void setA(String newA) { 
    a = newA; 
    setC(process(newA)); 
} 
+0

и downvoter, позаботиться об объяснении? –

+0

В отличие от вас, я делаю. Ваш класс является состоятельным, что не имеет никакого смысла в этом случае. Кроме того, ваш метод 'process' является' public', что означает, что он может быть вызван с '' a ', '" b "', '" c "и всем, что является' String', что полностью _unsafe_, потому что вы может передать что-то, что этот класс не знает. ;) –

+0

имеют класс statefull, это не только имеет смысл - это само определение Java Bean –

1

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

class IntegerValue { 

    private int value; 

    public IntegerValue(String str) { 
     value = parse(str); 
    } 

    public int parse(String str) { 
     return Integer.parseInt(str); 
    } 

    public int getValue() { 
     return value; 
    } 
} 

class HexValue extends IntegerValue { 

    private int radix = 16; 

    public HexValue(String str) { 
     super(str); 
    } 

    public int parse(String str) { 
     return Integer.parseInt(str, radix); 
    } 
} 

Попробуйте и вы увидите, что происходит, когда вы делаете

new HexValue("1F"); 

Лучше вы только позвоните final или private методы из конструктора.

Измените метод и назначить this.b вместо b

private void process(String a, String b){ 
    // Some complex operation on 'a' 
    // instance variable 'b' is initialized based on the result 
    this.b = result; 
} 
Смежные вопросы