Подтвердить что ты не робот

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

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

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 имеет пустой имп. of addSomeSpecialThings. Также этот пример является очень простым. Например, может быть больше различий в методе, поэтому было бы нелегко извлечь одну и ту же функциональность.

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

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

Куку.

4b9b3361

Ответ 1

Я бы поместил общий код в суперкласс 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");
        }
    }
}

Ответ 2

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

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

Ответ 3

Нет никакого факторинга в таком простом коде за счет удобочитаемости. Выбор композиции и выбора наследования будет иметь значение только с более реалистичным примером и будет более очевидным.