Статьи

Плохая структура программы: комплектация

Степени плохости

Многие программисты рассматривают зависимости исходного кода как циклические или некруглые, с циклическими зависимостями, представляющими Величайшее Воображаемое Зло (что, конечно, они делают), и некруглыми зависимостями, представляющими приемлемую, тусклую грань структуры исходного кода. Это второе представление не совсем верно. Цифровые боги не создают все некруглые зависимости одинаковыми.

На рисунке 1 показаны шесть методов, расположенных в двух независимых транзитивных зависимостях, цепочках методов: a () → b () → c () и d () → e () → f () . Прямые линии показывают зависимости вниз по странице, а изогнутые линии (которых пока нет) показывают зависимости вверх по странице.

Рисунок 1: Две переходные зависимости, связанные с их собственным бизнесом.

Рисунок 1: Две переходные зависимости, связанные с их собственным бизнесом.

Однако, как только в реальном мире две переходные зависимости оказываются близко друг к другу, зависимости между цепочками начинают появляться. На рисунке 2, например, метод e () получил признание метода c () .

Рисунок 2: зависимость между транзитивными зависимостями.

Рисунок 2: зависимость между транзитивными зависимостями.

Все идет нормально. Не круговая зависимость в поле зрения. Теперь рассмотрим транзитивную зависимость слева, которая образует вторую зависимость от своего аналога, причем f () вырастает зависимость от b () .

Рисунок 3: Кривый шар.

Рисунок 3: Кривый шар.

Вдруг что-то выглядит не так. Кажется, что переходная зависимость слева вызвала нездоровый интерес к правой. Две взаимосвязанные зависимости пересекаются, вызывая плетение или переплетение канатов. Рич Хикки классно направил свою внутреннюю Джейн Остин, чтобы воскресить архаичный глагол, описывающий именно такое переплетение: «Собирать». В его честь мы будем называть вышеуказанный тип зависимости a «Complectation» (мы не можем использовать «Complection», так как это означает «внешний вид кожи, особенно лица»), хотя, как ни странно, сложения имеют тенденцию давать программирует хилое завершение).

И это не просто эстетический момент. Сборники допускают математическое определение и объективный структурный анализ. Уменьшение волнового эффекта мотивирует всю большую структуру, и программисты могут измерить восприимчивость к волновому эффекту, посчитав количество методов, от которых зависит каждый метод; чем выше это значение, тем хуже структура. Это «Затронутый набор» программы. Обозначив каждый метод таким образом, мы можем перерисовать рисунок 3 следующим образом (в конечном счете, d () , например, зависит от 4 других методов):

Рисунок 4: Затронутый набор всех элементов.

Рисунок 4: Затронутый набор всех элементов.

На рисунке показан измененный набор из 12. Если, однако, программа может быть переписана немного поверхностнее, поднимая вызов f () с e () до d () , то рисунок 5, полностью исключающий сложность, получит ,

Рисунок 5: Уменьшение глубины.

Рисунок 5: Уменьшение глубины.

На рисунке 5 показано влияние 10, что на 17% меньше по сравнению с рисунком 4 за счет перемещения только одной зависимости метода.

Это, конечно, игрушечный пример, и часто программисту требуется именно то, что показано на рисунке 4. Но, по крайней мере, комплектации поднимают вопрос, и отсеивание ненужных комплексов может улучшить структуру. Ниже, например, два метода (сокращено для целей представления), createGraphicsContext () и colourBackground () , причем первый вызывает последний:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
private Graphics2D createGraphicsContext() {
    Graphics2D graphics2D = canvas.getBufferedImage().createGraphics();
    colourBackground(graphics2D);
    graphics2D.setColor(options.getColour(ColourTag.FOREGROUND));
    return graphics2D;
    }
 
    private void colourBackground(Graphics2D graphics2D) {
    BufferedImage bufferedImage = canvas.getBufferedImage();
    Color background = options.getColour(ColourTag.BACKGROUND);
    graphics2D.setColor(background);
    graphics2D.fillRect(0, 0, bufferedImage.getWidth(),
                bufferedImage.getHeight());
    }

Рисунок 6 изображает методы, как они появляются в дикой природе.

Рисунок 6: комплектация в реальном мире.

Рисунок 6: комплектация в реальном мире.

Композиция здесь возникает из-за того, что createGraphicsContext () и colourBackground () вызывают getBufferedImage () , когда тривиально переписать методы так, что createGraphicsContext () вызывает getBufferedImage () и передает возвращенный объект в colourBackground () (таким образом сокращая colourBackground () подвергается getBufferedImage () ):

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
private Graphics2D createGraphicsContext() {
    BufferedImage bufferedImage = canvas.getBufferedImage();
    Graphics2D graphics2D = bufferedImage.createGraphics();
    colourBackground(graphics2D, bufferedImage);
    graphics2D.setColor(options.getColour(ColourTag.FOREGROUND));
    return graphics2D;
    }
 
    private void colourBackground(Graphics2D graphics2D,
                  BufferedImage bufferedImage) {
    Color background = options.getColour(ColourTag.BACKGROUND);
    graphics2D.setColor(background);
    graphics2D.fillRect(0, 0, bufferedImage.getWidth(),
                bufferedImage.getHeight());
    }

Таким образом получая очень немного улучшенную фигуру 7.

Рисунок 7: Комплект исчез.

Рисунок 7: Комплект исчез.

Резюме

Сборники не самая большая проблема разработки программного обеспечения.

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

Но они показывают интересные самородки, которые можно найти во время панорамирования мерзлой воды структуры программы.