Самый чистый способ написания логически процедурного программного обеспечения на языке ОО


12

Я инженер-электрик, и я не знаю, что, черт возьми, я делаю. Пожалуйста, сохраните будущие сопровождающие моего кода.

Недавно я работал над рядом небольших программ (на C #), функциональность которых логически «процедурна». Например, одна из них - это программа, которая собирает информацию из разных баз данных, использует эту информацию для создания своего рода сводной страницы, распечатывает ее и затем завершает работу.

Логика, необходимая для всего этого, составляет около 2000 строк. Я, конечно, не хочу объединять все это в один, main()а затем «очищать» с помощью #regions, как это делали некоторые предыдущие разработчики (содрогание).

Вот некоторые вещи, которые я уже попробовал без особого удовлетворения:

Создайте статические утилиты для каждой грубой функциональности, например DatabaseInfoGetter, SummaryPageGenerator и PrintUtility. Сделать основную функцию похожей на:

int main()
{
    var thisThing = DatabaseInfoGetter.GetThis();
    var thatThing = DatabaseInfoGetter.GetThat();
    var summary = SummaryPageGenerator.GeneratePage(thisThing, thatThing);

    PrintUtility.Print(summary);
}

Для одной программы я даже пошел с интерфейсами

int main()
{
    /* pardon the psuedocode */

    List<Function> toDoList = new List<Function>();
    toDoList.Add(new DatabaseInfoGetter(serverUrl));
    toDoList.Add(new SummaryPageGenerator());
    toDoList.Add(new PrintUtility());

    foreach (Function f in toDoList)
        f.Do();
}

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

Какой хороший способ структурировать такие вещи, как это?


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


@Snowman, как новичок в написании больших кусков кода, обнадеживает то, что я не единственный человек, который сталкивается с такими проблемами. Мне понравился ваш ответ на этот вопрос. Помогает.
Ломбард

@ Ломбард идея снижения сложности до управляемого уровня - действительно хороший навык для развития. Никто не может понять большую базу кода, даже Супермен (возможно, Бэтмен, хотя). Поиск способов выразить идеи просто и самостоятельно документировать код имеет решающее значение.

«Недавно я работал над несколькими небольшими программами (на C #), функциональность которых логически« процедурна ». Например, одна из них - это программа, которая собирает информацию из разных баз данных, использует эту информацию для генерации своего рода сводки. страница, печатает его и затем выходит. ": Вы уверены, что не путаете" процедурный "с" императивным "? Как процедурные, так и объектно-ориентированные являются (могут быть) императивными, то есть они могут выражать операции, которые должны выполняться в последовательности.
Джорджио

Ответы:


18

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

Я думаю, что ваш первый инстинкт, код "эта вещь-вещь" в первом примере - верный путь. Это ясно и очевидно, что вы хотите сделать. Не беспокойтесь об эффективности кода, ясность гораздо важнее.

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

---- Постскриптум: OO Design Traps

Успешно работать с ОО-программированием может быть сложно. Есть даже некоторые люди, которые считают всю модель некорректной. Есть одна очень хорошая книга, которую я использовал, когда впервые изучал ОО-программирование, под названием Thinking in Java (сейчас в 4-м выпуске). Этот же автор имеет соответствующую книгу для C ++. На самом деле есть еще один вопрос о программистах, которые имеют дело с распространенными ошибками в объектно-ориентированном программировании .

Некоторые подводные камни сложны, но есть множество способов создать проблемы в самых простых формах. Например, пару лет назад в моей компании был стажер, который написал первую версию программного обеспечения, которое я унаследовал, и создал интерфейсы для всего, что могло быкогда-нибудь есть несколько реализаций. Конечно, в 98% случаев когда-либо существовала только одна реализация, поэтому код был загружен неиспользуемыми интерфейсами, что делало отладку очень раздражающей, потому что вы не можете вернуться назад через интерфейсный вызов, поэтому вы в конечном итоге должны выполнить текстовый поиск для реализации (хотя сейчас я использую IntelliJ, у него была функция «Показать все реализации», но тогда у меня не было этого). Здесь правило такое же, как и для процедурного программирования: всегда жестко кодируйте одну вещь. Только когда у вас есть две или более вещи, создайте абстракцию.

Подобный тип ошибки проектирования можно найти в Java Swing API. Они используют модель публикации-подписки для системы меню Swing. Это делает создание и отладку меню в Swing полным кошмаром. Ирония в том, что это совершенно бессмысленно. Практически никогда не бывает ситуации, в которой нескольким функциям потребуется «подписаться» на нажатие меню. Кроме того, публикация-подписка была полным пропуском там, потому что система меню обычно всегда используется. Это не значит, что функции подписываются, а затем отписываются случайным образом. Тот факт, что «профессиональные» разработчики в Sun допустили такую ​​ошибку, просто показывает, насколько легко даже профессионалам делать монументальные провалы в дизайне ОО.

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


1
+1 за «ясность намного важнее [чем эффективность]»
Стивен

Не могли бы вы указать мне ссылку, которая описывает, что представляет собой «плохо написанную ОО-программу», или добавить дополнительную информацию об этом в редактировании?
Ломбард

@ Ломбард, я так и сделал.
Тайлер Дерден

1

Первый подход в порядке. В процедурных языках, таких как C, стандартный подход состоит в том, чтобы разделить функциональность на пространства имен - использование статических классов подобно, DatabaseInfoGetterпо сути, одно и то же. Очевидно, что этот подход приводит к более простому, более модульному и более удобочитаемому / поддерживаемому коду, чем объединение всего в один класс, даже если все разбито на методы.

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

Если вы все еще боретесь со сложностью, вам, скорее всего, придется разбивать вашу программу еще дальше. Создайте больше уровней в иерархии - возможно, DatabaseInfoGetterнужно сослаться на некоторые другие классы, как ProductTableEntryили что-то. Вы пишете процедурный код, но помните, что вы используете C #, и ООП предоставляет вам набор инструментов для уменьшения сложности, например:

int main() 
{
    var thisthat = Database.GetThisThat();
}

public class ThisThatClass
{
    public String This;
    public String That;
}

public static class Database 
{
    public ThisThatClass GetThisThat()
    {
        return new ThisThatClass 
        {
            This = GetThis(),
            That = GetThat()
        };
    }

    public static String GetThis() 
    { 
        //stuff
    }
    public static String GetThat() 
    { 
        //stuff
    }

}

Также будьте осторожны со статическими классами. Классы базы данных являются хорошими кандидатами .. если у вас обычно есть одна база данных .. вы поняли идею.

В конечном счете, если вы думаете о математических функциях, а C # не подходит вашему стилю, попробуйте что-то вроде Scala, Haskell и т. Д. Они тоже классные.


0

Я отвечаю на 4 года позже, но когда вы пытаетесь сделать что-то процедурное на языке ОО, вы работаете над антипаттерном. Это не то, что вы должны сделать! Я нашел блог, который рассматривает этот антипаттерн и показывает решение для него, но это требует большого рефакторинга и изменения мышления. См. Процедурный код в объектно-ориентированном коде для получения более подробной информации.
Как вы упомянули «это» и «это», вы в основном предлагаете, чтобы у вас было два разных класса. Этот класс (плохое имя!) И Этот класс. И вы могли бы, например, перевести это:

var summary = SummaryPageGenerator.GeneratePage(thisThing, thatThing);

в это:

var summary = SummaryPageGenerator.GeneratePage(new This(DatabaseInfoGetter), new That(DatabaseInfoGetter));

Теперь было бы интересно увидеть эти более 2000 строк процедурного кода и определить, как различные части можно сгруппировать в отдельные классы и перенести различные процедуры в эти классы.
Каждый класс должен быть простым и ориентированным только на выполнение одной вещи. И мне кажется, что некоторые части кода уже имеют дело с суперклассами, которые на самом деле слишком велики, чтобы быть полезными. Например, DatabaseInfoGetter звучит странно. Что он получает в любом случае? Это звучит слишком обобщенно. Все же SummaryPageGenerator очень специфичен! И PrintUtility снова становится универсальным.
Итак, для начала вам следует избегать общих имен для ваших классов и начать использовать вместо них более конкретные имена. Например, класс PrintUtility будет больше классом Print с классом Header, классом Footer, классом TextBlock, классом Image и, возможно, каким-либо способом указать, как они будут передаваться в самой печати. ​​Все это может быть в Тем не менее, пространство имен PrintUtility .
Для базы данных похожая история. Даже если вы используете Entity Framework для доступа к базе данных, вы должны ограничить его тем, что вы используете, и разделить его на логические группы.
Но мне интересно, решаете ли вы эту проблему через 4 года? Если это так, то это мышление, которое необходимо изменить от «процедурной концепции» на «объектно-ориентированную концепцию». Что сложно, если у вас нет опыта ...


-3

Я считаю, что вы должны изменить свой код так, чтобы он был простым, последовательным и читабельным.

Я написал несколько сообщений в блоге на эту тему, и, поверьте мне, они просты для понимания: что такое хороший код

Просто: так же, как печатная плата содержит разные части, каждая из которых несет ответственность. Код должен быть разделен на более мелкие и простые части.

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

Удобочитаемость: не используйте аббревиатуры, если они широко не используются и не известны в домене, на который ориентировано программное обеспечение (mattnz), предпочитайте произносимые имена, которые понятны и просты для понимания.


1
Сокращения приемлемы, если они широко используются и известны в той области, на которую ориентировано программное обеспечение. Попробуйте написать программное обеспечение Air Traffic Control, не используя - у нас есть аббревиатуры, составленные из аббревиатур - никто не узнает, о чем вы говорите, если вы использовали полное имя. Такие вещи, как HTTP для сетей, ABS в автомобильной промышленности и т. Д. Вполне приемлемы.
Mattnz
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.