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

    Доброго времени суток. В этой статье я хочу показать не то, чем лучше структурное программирование или объектное, а то как нужно писать и там и там. И возможно это послужит выбором для тех, кто только хочет начать программировать и не знает какой язык выбрать и что может быть удобней. Я взял за пример языка 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 {
    	public void connect ( URL url );
    	public URLConnection getConnection ( ) throws IOException;
    }
    

    Здесь мы объявляет две функции, одна для соединения, другая для получения 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.

    файл main.c
    #include <stdio.h>
    #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 объявлена в другом файле.

    файл conn.h
    #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.

    файл http.c
    #include "conn.h"
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <stdio.h>
    
    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. Поэтому в этом файле будут все те данные, которые нужны для этого подключения.

    файл https.c
    #include "conn.h"
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <openssl/ssl.h>
    #include <stdio.h>
    
    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;
    }
    

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

    Similar posts

    Ads
    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More

    Comments 26

      +4

      Это какой-то прикол? )

        +4
        Если статья понравиться, то я буду писать продолжение в будущем.

        Не стóит.

          –1
          Ну ладно. Я рассматривал варианты. А почему не нравиться?
            +4
            Код какой-то… ритуальный. То есть, вроде функции и классы есть, но они бессмысленные. Ритуал соблюден, но смысла нет. Возможно, стоило придумать более практичный пример.
              +5
              Да что уж тут либеральничать-то? Код просто не рабочий. Поэтому ритуал который нужно провести — это закопать его, и не рассматривать.

              Как-бы, если мы хотим в нормальном ООП языке выполнить http запрос, то это будет выглядеть как-то так:

              println(«www.google.com».toURL().text)

              и все. Это груви, если что. Вот тут все как раз инкапсулировано — строка «www.google.com» умеет toURL(), а уже объект URL имеет метод getText() (который в груви можно записать просто как .text), и этот метод уже получает результат запроса.

              А тут написано строк 50 какой-то хрени, которая заканчивается conn.setDoOutput(true), а собственно выполнения http запроса в этом коде нет.

              Ну то есть, я написал в своей жизни десятки наверное http клиентов, и кучу подобного кода — но этот код просто какая-то непонятная ненужная фигня, которая делает вообще не то, что нужно. Как выполнить http запрос в java? Вот например, нормальная инструкция, и она совсем не об этом.

              https — это ведь тоже не о том, чтобы поменять несколько буковок в URL. Это о том, что бывают разные методы шифрования, сертификаты, и т.п., с которым нужно работать. А тут мы что видим, каст к HttpsUrlConnection, и все?

              P.S. Ну и русский язык тот еще… про тся/ться уже намекнули, но и остальное не лучше.
              +4

              Возможно потому, что приведенный пример — это не совсем ООП. Например, одна из задач ООП — инкапсуляция деталей от клиентского кода, клиенский код должен зависеть от абстракций и ничего не знать о подклассах. А здесь — знает, поэтому код выглядит сложным, и преимущества ООП непонятны. Инстанцирование подклассов нужно прятать от клиента в фабрике (паттерн Фабричный Метод). Про ООП нельзя рассказывать без описания паттернов, принципов типа SOLID и т.д.
              Ну и ряд других ошибок есть, которые скорее дискредитируют ООП.


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

                +1

                в c файлах переменные являются глобальными (тоже что и static внутри объекта). Поэтому работать с большим числом структур conn чем 1 вряд-ли получится.
                Чтобы этого избежать, нужно вносить все переменные внутрь структур. Придется изобретать полиморфизм в СИ. Но зачем?
                В с++ происходит в точности тоже самое, но более красиво


                полиморфизм в Си
                // headers
                struct conn {
                    void ( *connect ) ( const char *url );
                    char *( *read ) ( void );
                };
                
                struct conn_http {
                  // анонимная структура позволяет обращаться к ее полям, какбудто они есть в родительской структуре.
                  struct conn;
                  int sockfd;
                };
                
                struct conn_https {
                  struct conn;
                  int sockfd;
                  SSL *ssl;
                };
                
                // http.c
                struct conn_http* init_http () {
                        struct conn_http* self = zalloc(sizeof(struct conn_http));
                        if (self)
                        {
                        conn->connect = connect_http;
                        conn->read = read_http;
                        }
                      return self;
                }
                
                // https.c
                
                struct conn_https* init_https () {
                        struct conn_https* self = zalloc(sizeof(struct conn_https));
                        if (self)
                        {
                        conn->connect = connect_https;
                        conn->read = read_https;
                        }
                      return self;
                }
                
                // main.c
                struct conn* conn;
                
                int main ( int argc, char **argv ) {
                        // кастовать к "базовой структуре" не противозаконно, ее смещение внутри наследника равно 0
                    switch ( get_protocol ( ) ) {
                        case HTTP_PROTOCOL: conn = (struct conn*)init_http (); break;
                        case HTTPS_PROTOCOL: conn = (struct conn*)init_https (); break;
                        case ERROR_PROTOCOL: return -1;
                    }
                
                    conn->connect ( "www.google.com" );
                    char *data = conn->read ( );
                        free(conn);
                }
                  +10
                  Про ООП нельзя рассказывать без описания паттернов, принципов типа SOLID и т.д.

                  Как по мне, про ООП нужно рассказывать без описания паттернов, принципов типа SOLID и т.д., потому как ООП — это совсем не про паттерны и не про SOLID, и объединение их в одной теме, это грубое нарушение Single Responsibility Principle :)
                  Но лично мне сам подход «Пока я ещё наверное плохо знаю ООП, поэтому держите от меня статью по ООП» крайне не нравится. Не надо писать про то, что плохо знаешь.
                    +2
                    Как по мне, про ООП нужно рассказывать без описания паттернов, принципов типа SOLID и т.д., потому как ООП — это совсем не про паттерны и не про SOLID

                    А что бы вы тогда включили в краткий курс по ООП?


                    ООП — это же именно методология, а не синтаксис. Эти принципы входят в ООП изначально, а паттерны — это примеры их правильного применения.

                      0
                      Если изучать ООП, то в первую очередь как раз синтаксис и заезженные основные понятия, наследование, инкапсуляция, полиморфизм. Основная цель ООП, это структурировать бОльшие объемы кода, нежели позволяет СП без взрыва головного мозга. В таком виде оно появилось задолго до формулирования принципов SOLID, и до формулирования популярных паттернов.
                      А SOLID — это даже не методология. Это набор best practices, выработанный за пару десятилетий использования ООП. Преподавать их надо, но уже тогда, когда человек ООП владеет, и значит, способен понять, зачем ему эти практики, и какие сложности в организации разработки могут быть без них.
                        0

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


                        Ещё кстати стоит добавить к базовым принцип абстракции.


                        А что вы понимаете, скажем, под принципом полиморфизма? Как вы его сформулируете студентам без отсылок к SOLID, GRASP и паттернам?

                          +1
                          Если до конца следовать логике исторического развития ООП, тогда наследование и полиморфизм тоже не следует изучать,

                          Да я же не следую логике исторического развития ООП. Я просто отделяю базовые принципы от рекомендаций, суть которых понятна только при наличии практики, а в противном случае их надо было бы зубрить, не включая мозги :) И ладно ещё если речь идет о Single Responsibility, а попробуйте в рамках курса ООП объяснить студентам, которые несколько занятий назад понятие класса не знали, какую проблему решает IoC. Не зазубрить, дескать, делайте вот так, а именно понять, почему делать вот так, и какие проблемы будут в противном случае. Особенно в свете того, что на всех учебных примерах, которые они могли там писать в рамках учебного курса, как раз IoC был совершенно не к месту, и проблемы только бы создавал, а не решал.
                          А что вы понимаете, скажем, под принципом полиморфизма? Как вы его сформулируете студентам без отсылок к SOLID, GRASP и паттернам?

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

                        Паттерны — это прежде всего единые наименования для популярных решений. Многие из паттернов — способ обхода недостатков тех или иных языков. Например, с проникновение элементов ФП в ООП языки всё реже нужно имплементировать паттерны стратегия, можно просто колбэк передвть

                          0

                          А если стратегия stateful и поэтому у неё много методов, коллбэк справится? Или метод один, но кода на тысячу строк — так, что без разбивки на приватные методы код лямбды будет выглядеть как огромная простыня непонятного текста?

                            0

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

                              0

                              А я где-то подобное утверждал? )


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

                                0
                                >как функциональный подход инкапсулипует полиморфные сущности с состоянием

                                Состояние инкапсулируется через замыкания.
                      +3
                      тся
                    +2

                    ООП — это если есть сущность с именем, и её можно неким образом беспокоить через предоставленные сущностью(этой или другой) инструменты. Структурное программирование — не потому что структуры используются, а потому что структура программы прослеживается. Программа состоит из меньших блоков, взаимодействующих между собой. Обычно, блоками являются процедуры и функции. При логическом развитии идеи блоков до структур данных, операций над ними, и некоторой(к слову необязательной) компоновки их между собой появляется ООП. Развитие ООП до состояния, где данных как отдельных сущностей внутри программы не существуют, а остались только функции, оперирующие друг другом и внешним вводом — получили функциональное программирование.
                    Язык не причем. ООП на С -> GLib и Gstreamer. структурное программирование -> С++ без использования RAII, функциональщина в Java -> Rx и декораторы.

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


                      Не согласен. Взять Haskell или Erlang, там есть данные как отдельные сущности.
                      ФП — это когда функции являются First-Class Citizens, функции — чистые, и иммутабельность преобладает.
                      +2
                      Но этот способ конечно же придумал не я, я его сам вычитал из книги когда-то. И так https и http. Чтобы это сделать, нужно создать интерфейс. И к этому интерфейсу присваивать нужный класс, в зависимости от типа протокола. Я точно не помню, но вроде я где-то читал, что ООП вправляет мозги. Что это позволяет писать грамотный красивый код. Возможно так.

                      Вся статья выглядит как один большой несвязный кусок текста. Нет никакой структуры, никакого «газетного» стиля. После прочтения я понял только то, какой беспорядок происходил у автора в голове на момент написания этого «сочинения».
                        0
                        Не понятно о чем статья.
                        Вступление. Основная тема. Выводы.
                        Где это все?
                          –4
                          не обращай ни на кого внимание и пиши дальше, хорошее начало
                            –2
                            Да конечно.
                            +1
                            Текст писал человек, а если человек, то носитель русского языка? Уж больно, трудно читать.
                              0

                              Выбор языков для профориентиации начинающих программистов — ява и си?!
                              Ну нафиг, пардон май френч.


                              Си с его препроцессором, куцей системой типов, правом на ношение огнестрельного оружия и стрельбу в ногу.
                              Ява с её КоролевствомСуществительных.
                              Два, наверное, лучших языка для демотивации.


                              Да, промышленные стандарты, но.

                              Only users with full accounts can post comments. Log in, please.