Контекст и парадигмы программирования

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

Контекст это все и для ИИ и для человека!

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

Плохой вариант:

var d = DateTime.Now.AddDays(2);

Хороший вариант:

var deliveryDate = DateTime.Now.AddDays(2);

Давайте поговорим о контексте. Что же такое контекст? Контекст — это та логическая область, к которой мы соотносим какую-либо логическую единицу информации, например класс, сущность, переменную и т.д. В ООП языках примером является пространство имен, либо пакет, либо модуль.

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

Хороший вариант:

class Person
{
    public int Id { get; set; }
    public string FirstName { get; set; }
}

Плохой вариант:

class Person
{
    public int PersonId { get; set; }
    public string PersonFirstName { get; set; }
}

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

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

Принципы хорошего именования:

Объектно-ориентированный vs функциональный подход

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

Объектно-ориентированный стиль

ООП структурирует код вокруг объектов и их поведения. Пример на C# с инкапсуляцией:

class Order
{
    public DateTime DeliveryDate { get; private set; }
    
    public Order(DateTime deliveryDate)
    {
        DeliveryDate = deliveryDate;
    }
}

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

Наследование и полиморфизм

class Animal
{
    public virtual void Speak()
    {
        Console.WriteLine("Some sound");
    }
}

class Dog : Animal
{
    public override void Speak()
    {
        Console.WriteLine("Woof");
    }
}

class Cat : Animal
{
    public override void Speak()
    {
        Console.WriteLine("Meow");
    }
}

Использование полиморфизма:

List animals = new List { new Dog(), new Cat() };
foreach (var animal in animals)
{
    animal.Speak(); // Выведет "Woof" и "Meow"
}

Функциональный стиль

Функциональное программирование строится на чистых функциях и неизменяемых данных. Пример на Python с инкапсуляцией:

from datetime import datetime, timedelta

class Order:
    def __init__(self, delivery_date: datetime):
        self._delivery_date = delivery_date  # Инкапсуляция
    
    def get_delivery_date(self):
        return self._delivery_date

order = Order(datetime.now() + timedelta(days=2))
print(order.get_delivery_date())

Наследование и полиморфизм в функциональном стиле

Вместо классов можно использовать функции и замыкания:

def make_animal_speak(sound):
    def speak():
        print(sound)
    return speak

Dog = make_animal_speak("Woof")
Cat = make_animal_speak("Meow")

animals = [Dog, Cat]
for animal in animals:
    animal()  # Выведет "Woof" и "Meow"

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

Когда использовать ООП, а когда функциональный стиль?

Заключение

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

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

@S1908
06.03.2025 11:15 UTC
Первоисточник

Комментарии

@Dhwtj
06.03.2025 07:56 UTC
0

Все же не контекст, а описание предметной области

Лично я взял за правило описывать предметную область русскими именами, если только в ТЗ нет однозначного перевода терминов как говорит бизнес на английские термины программы

В ТЗ этого нет? (а никогда нет) - значит и в коде нет!

Соответствие должно быть так чтобы в коде по Ctrl+F искать бизнес термин.

Потому что медицинский термин из 10 слов каждый будет переводить по разному и со словарем.

Весь домен у меня в русскоязычных классах и их методах

А технологическая обвязка как обычно - англоязычная

class Гражданин
{
    public int Id { get; set; }
    public string Имя { get; set; }
}

@Naf2000
06.03.2025 08:20 UTC
0

Не устаете переключать раскладку? Id почему не переведено?

06.03.2025 11:41 UTC
0

Очевидно же: id это не бизнес термин.

Домен меньше остального кода.

Да флаг вам в руки:

"ЗаболевшиеОРВИ"

Что будете искать в коде?

  1. ArviPatients

  2. PatientsWithArvi

  3. PatientsWithAcuteViralRespiratoryInfection

  4. AcuteRespiratoryInfectionCases

  5. ViralUpperRespiratoryInfectionPatients

  6. RespiratoryViralInfectionPatients

  7. PatientsWithRespiratoryVirusInfection

  8. PeopleAffectedByArvi

  9. ArviInfectedPatients

  10. PatientsDiagnosedWithArvi

  11. ArviPatientCount

  12. PersonsSufferingFromArvi

  13. ActiveArviCases

  14. IndividualsWithArvi

ARVI: 'A' - acute

Но многие поставили бы О

И вариант посложнее

"Хронический рецидивирующий афтозный стоматит полости рта"

Несколько возможных вариантов перевода на английский (PascalCase):

  1. ChronicRecurrentAphthousOralStomatitis

  2. RecurrentChronicAphthousMouthStomatitis

  3. ChronicRelapsingAphthousOralStomatitis

  4. RecurringChronicAphthousMouthInflammation

  5. ChronicRecurrentOralAphthousStomatitis

06.03.2025 19:57 UTC
0

А вы все заболевания вносите как отдельные сущности? Параметризация/каталогизация не спасают?

@BigVal
06.03.2025 09:47 UTC
0

программировали на 1С? :-)

@SolidSnack
07.03.2025 05:55 UTC
0

Функциональный стиль хорош для работы с данными, параллельных вычислений и минимизации побочных эффектов. - пишет автор
Может я не правильно прочитал статью (перевод) https://habr.com/ru/articles/570642/ Но помоему здесь написано про то что функциональное программирование как раз и создает эти side effects

@vabka
07.03.2025 08:22 UTC
0

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

09.03.2025 06:48 UTC
0

Каким образом?

09.03.2025 08:10 UTC
0

Я имел в виду, что говорить "ФП создаёт сайд эффекты" - это всё равно что говорить "ПДД создаёт нарушителей скорости".

От того что у тебя в языке нет явного разделения на чистые функции и функции с сайд эффектами - это не значит, что у тебя нет сайд эффектов.

Ну вот на примере хаскеля - для побочных эффектов есть монада IO.

Если без хаскеля, то ограничениями синтаксиса. Иммутабельность по умолчанию, отсутствие какого-то глобального состояния, отсутствие объектов как в ООП, где методы имеют полный доступ ко всем полям объекта. На примере Rust - у замыканий есть типы Fn, FnMut, FnOnce, которые тоже позволяют ограничить сайд эффекты в них.

09.03.2025 11:52 UTC
0

Хорошо, согласен, ФП не создает side effect. Побочный эффект присущ по сути любому языку программирования. Но разве тогда не получается что ООП как-раз шаг в сторону по борьбе с побочными эффектами?

09.03.2025 12:37 UTC
0

Что вы имеете в виду под "шаг в сторону"?

10.03.2025 17:59 UTC
0

Логический шаг к возможности контролировать эффекты функций при выполнении программ, на уровне языка программирования

10.03.2025 18:26 UTC
0

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

Максимум какой-нибудь constexpr из плюсов позволяет пометить чистые функции.

10.03.2025 18:40 UTC
0

В ООП можно ограничить изменения данных до 1 файла (класса). Контролировать код на уровне объектов, за счет магических методов например

10.03.2025 18:57 UTC
0

А как ты ограничишь влияние, если ты в любом месте в коде можешь, например, обратиться к файловой системе или какому-нибудь public static полю?

И что такое "магические методы"?

10.03.2025 19:31 UTC
0

Ты можешь обращаться к файловой системе через объект который выражает файловую систему в коде. И опять же с помощью магических методов контролировать работу с ним. Магические методы помогают программно задать логику объекта при работе с ним в клиентском коде. Например (в зависимости от языка): создание, вызов объекта как функцию, вызов как строку, сравнение объектов и еще чуть больше разных операций

11.03.2025 19:00 UTC
0

Всё ещё не понимаю магию.

И инкапсуляция никак не отменяет побочные эффекты.

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

Как я могу на уровне типов описать условно "эта функция имеет какой-то побочный эффект" или "эта функция не имеет побочных эффектов"?

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

А побочный эффект, в общем случае, запрещает подобное.

11.03.2025 21:17 UTC
0

С помощью объектов и контроля за ними можете гарантировать что любая функция в коде не изменит данных.

Все ООПшные прелести не завязаны на интерфейсе, само наличие интерфейсов и фабрик не делает по определению архитектуру качественной. Можно написать и плохую фабрику.

Чем плох интерфейс? Если вам не надо расширять, можно и без интерфейса обойтись

@S1908
17.04.2025 19:38 UTC
0

Приветствую всех! Вау я даже не думал что она опубликуется это моя первая статья)

@diakin
13.06.2025 13:46 UTC
0

Да все норм )

@diakin
13.06.2025 13:47 UTC
0

class Person { public int PersonId { get; set; }

Зато PersonId искать в тексте легче, чем кучу просто Id.