Как определить уровни абстракции

35

Сегодня я читал книгу под названием «Чистый код» и наткнулся на абзац, где автор говорил об уровнях абстракции для функции, он классифицировал некоторый код как низкий / средний / высокий уровень абстракции.

Мой вопрос: каковы критерии определения уровня абстракции?

Я цитирую абзац из книги:

Чтобы убедиться, что наши функции выполняют «одну вещь», мы должны убедиться, что все операторы в нашей функции находятся на одном уровне абстракции. Легко увидеть, как листинг 3-1 нарушает это правило. Там есть понятия, которые находятся на очень высоком уровне абстракции, такие как getHtml (); другие, которые находятся на промежуточном уровне абстракции, такие как: String pagePathName = PathParser.render (pagePath); и еще другие, которые находятся на удивительно низком уровне, такие как: .append ("\ n").

OKAN
источник
2
Связанный: Что такое абстракция? ,
Адам Лир

Ответы:

27

Автор объясняет, что в подразделе «Чтение кода сверху вниз» части, в которой говорится об абстракциях (шахта иерархического отступа):

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

  • Чтобы включить настройки и разборки, мы включаем настройки, затем мы включаем содержимое тестовой страницы, а затем мы включаем разборки.
    • Чтобы включить настройки, мы включаем установку комплекта, если это комплект, тогда мы включаем обычную настройку.
      • Чтобы включить настройку пакета, мы ищем в родительской иерархии страницу «SuiteSetUp» и добавляем оператор включения с путем к этой странице.
        • Для поиска родителя ...

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

public void CreateTestPage()
{
    IncludeSetups();
    IncludeTestPageContent();
    IncludeTeardowns();
}

public void IncludeSetups()
{
    if(this.IsSuite())
    {
        IncludeSuiteSetup();
    }

    IncludeRegularSetup();
}

public void IncludeSuiteSetup()
{
    var parentPage = FindParentSuitePage();

    // add include statement with the path of the parentPage
}

И так далее. Каждый раз, когда вы углубляетесь в иерархию функций, вы должны менять уровни абстракции. В приведенном выше примере IncludeSetups,IncludeTestPageContent и IncludeTeardownsвсе на том же уровне абстракции.

В примере, приведенном в книге, автор предлагает разбить большую функцию на более мелкие, которые очень специфичны и выполняют только одну функцию. Если все сделано правильно, рефакторированная функция будет выглядеть аналогично приведенным здесь примерам. (Реорганизованная версия приведена в листинге 3-7 в книге.)

Адам Лир
источник
10

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

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

То же самое с программным обеспечением. Всякий раз, когда вы программируете, как советует книга, вы должны думать о своем программном обеспечении как о слоях. Данная программа может легко иметь более ста слоев. Внизу у вас могут быть инструкции по сборке, которые выполняются на процессоре, на более высоком уровне эти инструкции могут быть объединены для формирования подпрограмм дискового ввода-вывода, но на более высоком уровне вам не нужно работать с диском I / O напрямую, потому что вы можете использовать функции Windows для простого открытия / чтения / записи / поиска / закрытия файла. Все это абстракции еще до того, как вы перейдете к собственному коду приложения.

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

Ключом к каждому из этих уровней абстракции является то, что каждый из них скрывает детали, представленные предыдущим уровнем (уровнями), и представляет очень чистый интерфейс, который будет использоваться следующим уровнем вверх. Чтобы открыть файл, вам не нужно знать, как писать отдельные сектора или какие аппаратные прерывания обрабатываются. Но если вы начнете перемещаться по цепочке уровня абстракции, вы определенно сможете проследить от вызова функции Write (), вплоть до точной инструкции, отправляемой контроллеру жесткого диска.

Автор говорит вам, что когда вы определяете класс или функцию, подумайте, какой вы слой. Если у вас есть класс, который управляет подсистемами и объектами пользователя, тот же класс не должен выполнять низкоуровневые манипуляции со строками или содержать целый набор переменных только для выполнения вызовов сокетов. Это было бы нарушением пересечения уровней абстракции, а также наличия одного класса / функции, делающей только одно (SRP - принцип единой ответственности).

DXM
источник
2

Мой вопрос: каковы критерии определения уровня абстракции?

Уровень абстракции должен быть очевидным. Это абстрактно, если это часть проблемной области, а не часть языка программирования. Трудно быть более ясным, чем "очень абстрактные" == "не реальные" == "проблемные области". И "не абстрактно == конкретная == часть языка". Это должно быть тривиально, чтобы определить уровень абстракции. Там не должно быть никакой тонкости вообще.

.append("\n")не абстрактно. Он просто помещает символ в строку. Это было бы конкретно. Не абстрактно.

String pagePathName = PathParser.render(pagePath);имеет дело со строками. Конкретные вещи. Частично о конкретных особенностях языка программирования. Частичная работа с абстрактными понятиями «путь» и «парсер».

getHtml(); Аннотация. Имеет дело с "Разметкой" и вещами, которые не являются тривиальными, конкретными, языковыми особенностями.

Абстракт == не языковая особенность.

Конкретный == языковая особенность.

С. Лотт
источник
1
Если вы определяете абстрактный как качество, которое описывает те вещи, которые создает программист, то есть сгенерированные программистом абстракции, то я склонен согласиться с вашими определениями слов. Но все языки программирования - это абстракции над чем-то более конкретным. Выбор языка программирования в значительной степени определяет, с какого уровня абстракции вы начинаете.
Роберт Харви
1

Я думаю, что уровень абстракции прост ... если строка кода напрямую не реализует единственную ответственность метода, это другой уровень абстракции. Например, если мой метод называется SaveChangedCustomers () и в качестве параметра принимает список ВСЕХ клиентов, единственной обязанностью является сохранение всех клиентов в списке, которые были изменены:

foreach(var customer in allCustomers)
{
    if (CustomerIsChanged(customer)
        customer.Save();
}

Часто вместо вызова метода CustomerIsChanged () вы найдете логику, чтобы определить, изменился ли клиент, встроенный в цикл foreach. Определение того, изменилась ли запись клиента, НЕ является обязанностью этого метода! Это другой уровень абстракции. Но что, если логика для такого определения - всего одна строка кода? Это не важно !!! Это другой уровень абстракции и должен быть за пределами этого метода.

user7542047
источник