Преимущества ооп перед структурным программированием

Чистая архитектура. Часть II — Парадигмы программирования

Эта серия статей – вольный и очень краткий пересказ книги Роберта Мартина (Дяди Боба) «Чистая Архитектура», выпущенной в 2018 году. Начало тут.

Парадигмы программирования

Дисциплину, которая в дальнейшем стала называться программированием, зародил Алан Тьюринг в 1938 году. В 1945 он уже писал полноценные программы, которые работали на реальном железе.

Первый компилятор был придуман в 1951 году Грейс Хоппер (бабушка с татуировкой Кобола). Потом начали создаваться языки программирования.

Обзор парадигм

Существует три основных парадигмы: структурное, объектно-ориентированное и функциональное. Интересно, что сначала было открыто функциональное, потом объектно-ориентированное, и только потом структурное программирование, но применяться повсеместно на практике они стали в обратном порядке.

Структурное программирование было открыто Дейкстрой в 1968 году. Он понял, что goto – это зло, и программы должны строиться из трёх базовых структур: последовательности, ветвления и цикла.

Объектно-ориентированное программирование было открыто в 1966 году.

Функциональное программирование открыто в 1936 году, когда Чёрч придумал лямбда-исчисление. Первый функциональный язык LISP был создан в 1958 году Джоном МакКарти.

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

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

Структурное программирование

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

Чтобы решить эту проблему, Дейсктра решил сделать написание программ подобно математическим доказательствам, которые также организованы в иерархии. Он понял, что если в программах использовать только if, do, while, то тогда такие программы можно легко рекурсивно разделять на более мелкие единицы, которые в свою очередь уже легко доказуемы.

С тех пор оператора goto не стало практически ни в одном языке программирования.

Таким образом, структурное программирование позволяет делать функциональную декомпозицию.

Однако на практике мало кто реально применял аналогию с теоремами для доказательства корректности программ, потому что это слишком накладно. В реальном программировании стал популярным более «лёгкий» вариант: тесты. Тесты не могут доказать корректности программ, но могут доказать их некорректность. Однако на практике, если использовать достаточно большое количество тестов, этого может быть вполне достаточно.

Объектно-ориентированное программирование

ООП – это парадигма, которая характеризуется наличием инкапсуляции, наследования и полиморфизма.

Инкапсуляция позволяет открыть только ту часть функций и данных, которая нужна для внешних пользователей, а остальное спрятать внутри класса.

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

Читайте также:  Контроллер скуд z5r программирование

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

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

Полиморфизм – это ключевое свойство ООП для построения грамотной архитектуры. Он позволяет сделать модуль независимым от конкретной реализации (реализаций) интерфейса. Этот принцип называется инверсией зависимостей, на котором основаны все плагинные системы.

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

Любая зависимость всегда может быть инвертирована. В этом и есть мощь ООП.

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

Функциональное программирование

В основе функционального программирования лежит запрет на изменение переменных. Если переменная однажды проинициализирована, её значение так и остаётся неизменным.

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

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

Интересным подходом для уменьшения изменяемых данных является Event Sourcing. В нём мы храним не сами данные, а историю событий, которые привели к изменениям этих данных. Так как в лог событий можно только дописывать, это означает, что все старые события уже нельзя изменить. Чтобы получить текущее состояние данных, нужно просто воспроизвести весь лог. Для оптимизации можно использовать снапшоты, которые делаются, допустим, раз в день.

Заключение

Таким образом, каждая из трёх парадигм ограничивает нас в чём-то:

  • Структурное отнимает у нас возможность вставить goto где угодно.
  • ООП не позволяет нам доступаться до скрытых членов классов и навязывает нам инверсию зависимостей.
  • ФП запрещает изменять переменные.

Источник

Rabochy_stol / 11 преимущества ооп

Во многих учебниках выделяют такие основные идеи ООП как наследование, инкапсуляция и полиморфизм. Заключаются они примерно в следующем:

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

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

Читайте также:  Язык программирования питон области применения

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

В связи со своими особенностями объектно-ориентированное программирование имеет ряд преимуществ перед структурным (и др.) программированием. Выделим некоторые из них:

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

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

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

Источник

Структурное против ООП программирование

Доброго времени суток. В этой статье я хочу показать не то, чем лучше структурное программирование или объектное, а то как нужно писать и там и там. И возможно это послужит выбором для тех, кто только хочет начать программировать и не знает какой язык выбрать и что может быть удобней. Я взял за пример языка C и JAVA.

Начну с первого, что приходит на ум. Это вроде не паттерн, но в ООП это приходится иногда использовать. Пишу сначала пример на java. Допустим нам нужно создать класс для двух видов реализации. К примеру нам нужно создать класс, чтобы подключаться к сайту по http и https. Это маленький пример, например этот же способ можно использовать, когда ты хочешь для android рисовать и в Opengl 2.0 es и Opengl 3.0 es. В этом случае будет больше кода и если сделать это таким способом, который приведу я, то вид кода будет нормальный. Но этот способ конечно же придумал не я, я его сам вычитал из книги когда-то. И так https и http. Чтобы это сделать, нужно создать интерфейс. И к этому интерфейсу присваивать нужный класс, в зависимости от типа протокола. Я точно не помню, но вроде я где-то читал, что ООП вправляет мозги. Что это позволяет писать грамотный красивый код. Возможно так. Но я почти в ООП не программирую и поэтому может это я так много кода пишу, а настоящий программист сделает более лаконичный код. Но вот хочу показать пример. Вот это интерфейс java.

import java.net.*; import java.io.*; interface IProtocol

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

import java.net.*; import java.io.*; public class Http implements IProtocol < public URL url; public void connect ( URL url ) < this.url = url; >public HttpURLConnection getConnection ( ) throws IOException < return ( HttpURLConnection ) url.openConnection( ); >> 
import java.net.*; import javax.net.ssl.*; import java.io.*; public class Https implements IProtocol < public URL url; public void connect ( URL url ) < this.url = url; >public HttpsURLConnection getConnection ( ) throws IOException < return ( HttpsURLConnection ) url.openConnection ( ); >> 

Сколько кода нужно писать в connect и getConnection не важно. Для примера я выбрал мало кода, но его может быть много, если например Opengl es программировать. Но это удобно. Итак, осталась функция main.

import java.net.*; import java.io.*; public class Main < public static void main ( String[] args ) < URL url = null; try < url = new URL ( "https://www.google.com" ); >catch ( MalformedURLException e ) < return; >String protocol = url.getProtocol ( ); IProtocol prot = null; switch ( protocol ) < case "http": prot = new Http ( ); break; case "https": prot = new Https ( ); break; default: return; >prot.connect ( url ); URLConnection conn = null; try < conn = prot.getConnection ( ); >catch ( IOException e ) < return; >conn.setDoOutput ( true ); > > 

На C можно воспользоваться curl и не писать много кода, но как бы этот пример можно было решить средствами C? В C есть указатели — это его сила. А вот пример на C — функция main.

#include #include "conn.h" struct conn conn; #define HTTP_PROTOCOL 1 #define HTTPS_PROTOCOL 2 #define ERROR_PROTOCOL -1 static int get_protocol ( void ) < return HTTP_PROTOCOL; >int main ( int argc, char **argv ) < switch ( get_protocol ( ) ) < case HTTP_PROTOCOL: init_http ( &conn ); break; case HTTPS_PROTOCOL: init_https ( &conn ); break; case ERROR_PROTOCOL: return -1; >conn.connect ( "www.google.com" ); char *data = conn.read ( ); > 

Структура conn объявлена в другом файле.

#ifndef __CONN__ #define __CONN__ struct conn < void ( *connect ) ( const char *url ); char *( *read ) ( void ); >; void init_http ( struct conn *conn ); void init_https ( struct conn *conn ); #endif 

Для init_http выделен целый файл с областью видимости, которые нужны для http.

#include "conn.h" #include #include #include static int sockfd; static void connect_http ( const char *url ) < sockfd = socket ( AF_INET, SOCK_STREAM, 0 ); >static char *read_http ( void ) < return NULL; >void init_http ( struct conn *conn ) < conn->connect = connect_http; conn->read = read_http; > 

Для init_https дополнительно нужен ssl. Поэтому в этом файле будут все те данные, которые нужны для этого подключения.

#include "conn.h" #include #include #include #include static int sockfd; static SSL *ssl; static void connect_https ( const char *url ) < sockfd = socket ( AF_INET, SOCK_STREAM, 0 ); >static char *read_https ( void ) < return NULL; >void init_https ( struct conn *conn ) < conn->connect = connect_https; conn->read = read_https; > 

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

Читайте также:  Doorhan пульт программирование от другого пульта

Источник

Оцените статью