Хорошая стратегия, чтобы избежать дублирования кода - PullRequest
14 голосов
/ 27 ноября 2010

Допустим, у меня есть следующий сценарий:

public class A {
    public String createString(final String value){
        if (value ==  null){
            throw new NullPointerException("value must NOT be null.");
        }
        final StringBuffer sb = new StringBuffer();
        sb.append("A");
        sb.append("B");
        sb.append("C");
        if (value.length() > 3){
            sb.append("D");
            sb.append("E");
        }
        sb.append("Z");
        return sb.toString();   
    }
}

И еще один класс, который должен выполнить аналогичную задачу:

public class B {
    public String createString(final String value){
        if (value ==  null){
            throw new NullPointerException("value must NOT be null.");
        }
        final StringBuffer sb = new StringBuffer();
        sb.append("A");
        sb.append("B");
        sb.append("C");
        sb.append("Z");
        return sb.toString();   
    }
}

Что было бы хорошей стратегией, чтобы избежать дублирования кода?До сих пор я придумал, что класс B, который имеет функциональность подмножества A и, следовательно, должен выходить за пределы класса A, и те же задачи должны быть преобразованы в защищенные методы (при условии, что они находятся в одном пакете).Вот как это будет выглядеть:

public class A {
    public String createString(final String value){
        final StringBuffer sb = createTheFirstPart(value);
        if (value.length() > 3){
            sb.append("D");
            sb.append("E");
        }
        createTheLastPart(sb);
        return sb.toString();   
    }

    protected void createTheLastPart(final StringBuffer sb) {
        sb.append("Z");
    }

    protected StringBuffer createTheFirstPart(final String value) {
        if (value ==  null){
            throw new NullPointerException("value must NOT be null.");
        }
        final StringBuffer sb = new StringBuffer();
        sb.append("A");
        sb.append("B");
        sb.append("C");
        return sb;
    }
}

И класс B:

public class B extends A {
    public String createString(final String value){
        final StringBuffer sb = createTheFirstPart(value);
        createTheLastPart(sb);
        return sb.toString();   
    }
}

Другое возможное решение будет примерно таким:

public class A {
    public String createString(final String value){
        if (value ==  null){
            throw new NullPointerException("value must NOT be null.");
        }
        final StringBuffer sb = new StringBuffer();
        sb.append("A");
        sb.append("B");
        sb.append("C");
        addSomeSpecialThings(value, sb);
        sb.append("Z");
        return sb.toString();   
    }

    protected void addSomeSpecialThings(final String value, final StringBuffer sb) {
        if (value.length() > 3){
            sb.append("D");
            sb.append("E");
        }
    }
}

икласс B:

public class B extends A {
    public String createString(final String value){
        return super.createString(value);
    }

    protected void addSomeSpecialThings(final String value, final StringBuffer sb) {
        // do nothing
    }
}

Очевидно, что это не так хорошо, потому что B имеет пустой импл.из addSomeSpecialThings.Также этот пример очень простой.Например, в методе может быть больше различий, поэтому будет нелегко извлечь ту же функциональность.

Мои решения основаны на наследовании, может быть, было бы лучше сделать это с помощью композиции.Я также подумал, что это, вероятно, кандидат на стратегию.

Ну, каков наилучший подход к такой проблеме?Заранее спасибо за любую помощь.

Куку.

Ответы [ 3 ]

11 голосов
/ 27 ноября 2010

Я бы поместил общий код в суперкласс A и B:

public abstract class SomeName {
    public final String createString(final String value){
        if (value ==  null){
            throw new NullPointerException("value must NOT be null.");
        }
        final StringBuffer sb = new StringBuffer();
        sb.append("A");
        sb.append("B");
        sb.append("C");
        addSomeSpecialThings(value, sb);
        sb.append("Z");
        return sb.toString();   
    }

    protected abstract void addSomeSpecialThings(final String value,
            final StringBuffer sb);
}

Тогда B будет выглядеть так:

public class B extends SomeName {
    protected void addSomeSpecialThings(final String value,
            final StringBuffer sb) {}
}

И это будет A:

public class A extends SomeName {
    protected void addSomeSpecialThings(final String value, final StringBuffer sb) {
        if (value.length() > 3){
            sb.append("D");
            sb.append("E");
        }
    }
}
3 голосов
/ 27 ноября 2010

Описанная ситуация довольно проста. Я думаю, что с наследованием все в порядке, но я бы предложил создать базовый класс с пустыми реализованными addSomeSpecialThings, а затем наследовать два класса, A и B, и переопределить метод.

Паттерн стратегии уместен, но не в таком простом случае. В двух ситуациях слишком мало для накладных расходов, необходимых для реализации шаблона.

1 голос
/ 27 ноября 2010

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...