3 место Начинаем кодить на c# (часть 2). Что такое ООП? Принципы ООП. Примеры в Zennoposter

Brabus_bots

Client
Регистрация
13.04.2019
Сообщения
656
Благодарностей
434
Баллы
63
Всех приветствую, на связи Brabus.

В этой статье я хотел бы продолжить цепочку статей по изучению языка программирования c# в рамках Zennoposter.

Если вы не читали мою предыдущую конкурсную статью об изучении c#, которая заняла 3е место, то советую сперва прочитать её:
Начинаем кодить на c#

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

У данной статьи есть минималный порог входа:
1) Нужно знать азы c#, которые я описал в первой статье по c#
2) Нужно уметь писать базовые шаблоны на Zennoposter

Если вы не усвоили минималный порог, то понять данную статью вам будет не просто)

ООП - объектно-ориентированное программирование. Из самого определения нам становится понятно, что данная методология программирования направлена на представление кода в виде объектов (сущностей), которые по заданной нами логике взаимодействуют с кодом и друг с другом.

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

Я не зря в предыдущей статье о c# проговорил, что она посвящена ПРОЦЕДУРНОМУ подходу к программированию. Чтобы понять смысл ООП, нам сперва нужно вспомнить, что же такое процедурное программирование.

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

107190

Пример простого процедурнго кода:
//Создали переменную number со значением равным нулю
int number = 0;

//Выводим значение переменной в ЛОГ
project.SendInfoToLog("Значение переменной number: " + number.ToString());

//Прибавили к переменной единицу
number = number + 1;

//Выводим значение переменной в ЛОГ
project.SendInfoToLog("Значение переменной number: " + number.ToString());

//Прибавили к переменной единицу еще раз
number = number + 1;

//Выводим значение переменной в ЛОГ
project.SendInfoToLog("Значение переменной number: " + number.ToString());

//Прибавили к переменной единицу еще раз
number++;

//Выводим значение переменной в ЛОГ
project.SendInfoToLog("Значение переменной number: " + number.ToString());
107191

Другими словами - код программы выполняется снизу вверх. От первой строчки кода банально вниз, до последней строчки кода.

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

На помощь программистам пришло ООП. Оно позволило раздувать функционал прогаммы до любого обозримого размера и делать этот функционал вполне себе обслуживаемым и масштабируемым.

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

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

Классы и объекты. Что это такое?

Класс в ООП - это некий шаблон или модель, по которому создаются объекты.

Я профи в рассылках, поэтому давайте разберем это на примере моих любимых вебмейлинг-рассылок. Все знают, что существует множество почтовиков, но мы возьмем 3 самых крупных в СНГ - mail, gmail и yandex. Любой из нас в любое время может создать почту в любом из этих почтовиков.

Будут ли отличатся наши аккаунты по функционалу или внешнему виду в рамках одного почтовика? - Нет, не будут. Любой аккаунт, ваш или соседа, будет иметь одни и те же поля для заполнения (ФИО, возраст, ГЕО, логин, пароль и т.п.), а также будет иметь один и тот же набор функционала (отправить письмо, получить письмо, удалить письмо и т.п.).

Будут ли отличатся между собой аккаунты разных почтовиков? - Конечно будут, хотя у них и есть похожий функционал (отправка письма, чтения письма и т.п.). У того же аккаунта gmail будет доступ к ютубу, у аккаунта yandex будет доступ к dzen, а у аккаунта mail будет доступ к Моему Миру.

Делаем вывод, что в этом примере mail, gmail и yandex - это классы, или другими словами - шаблоны, по которому создаются объекты, т.е. аккаунты. В каждом классе зашита своя логика, на основе которой создаются объекты.

107192


Объекты взаимодействуют с кодом нашей программы. Также объекты разных классов могут взаимодействовать друг с другом. Аккаунт mail может отправить письмо на аккаунт gmail. Аккаунт yandex может удалить письмо от mail.

Давайте для примера создадим классы Mail, Gmail и Yandex и создадим объекты этих классов.
Пример создания классов (общий код):
public class Mail
{
    //тут какая-то реализация класса Mail
}

public class Gmail
{
    //тут какая-то реализация класса Gmail
}

public class Yandex
{
    //тут какая-то реализация класса Yandex
}
Пример создание объектов (кубик c#):
//создадим 3 объекта, по одному на каждый класс
Mail mail_account_1 = new Mail();

Gmail gmail_account_1 = new Gmail();

Yandex mail_account_1 = new Yandex();


//создадим еще 3 объекта, по одному на каждый класс
Yandex mail_account_2 = new Yandex();

Mail mail_account_2 = new Mail();

Gmail gmail_account_2 = new Gmail();


/*
Разберем формат записи на примере одной строки

Mail mail_account_1 = new Mail();

Mail - это тип объекта, который равняется названию класса

mail_account_1 - это название объекта, который мы создаём

new Mail() - означает, что мы хотим создать новый объект

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

int nubmer_1 = 10;

*/

На практике такой код не делал бы ровно делом ничего. Мы просто создали классы-пустышки и объекты-пустышки. Давайте чуть-чуть добавим реализма в наш пример по созданию класса Mail.

Пример создания класса Mail с переменными и конструктором (общий код):
public class Mail
{
    //У любого аккаунта есть логин и пароль

    public string login { get; set; }
    public string password { get; set; }

    public Mail(string Login, string Password)
    {
        login = Login;
        password = Password;
    }
}

/*
    get и set (от англ. взять и дать, получить и установить)
    Дают нам возможность установить или получить значения переменных
    в любом удобном месте кода.

    public Mail(string Login, string Password)
    Этот блок называется Конструктор. Он выполняется в коде при создании объекта
    и позволяет установить переменным какие-либо значения. В нашем случае - для
    переменных login и password. Обратите внимание, что название контруктора равняется
    названию класса.
   
    Конструктор принимет на вход значения Login и Password и передаёт данные значения
    во внутренние переменные класса login и password. Названия отличаются первой заглавной
    буквой не просто так, иначе была бы путанница и компилятор выдал бы ошибку.
*/
Пример создания объекта класса Mail с использованием конструктора. Пример использования get и set (кубик c#):
Mail mailAccount1 = new Mail("[email protected]", "dima123");
/*
Благодаря конструктору мы установили значение логина и пароля
прямо во время создания объекта.
*/


project.SendInfoToLog("Логин: " + mailAccount1.login);
project.SendInfoToLog("Пароль: " + mailAccount1.password);
/*
Благодаря методу get мы можем получить значения переменных класса,
и, как пример, вывести их в ЛОГ
*/


mailAccount1.login = "[email protected]";
mailAccount1.password = "vasya123";
/*
Благодаря методу set мы можем установить новые значения для
переменных класса
*/


project.SendInfoToLog("Новый логин: " + mailAccount1.login);
project.SendInfoToLog("Новый пароль: " + mailAccount1.password);
/*
Благодаря методу get снова заглянем в значения внутренних переменных класса
login и password
*/
107198


Я хочу заострить еще немного внимания на конструкторе, а именно на отличие переменных Login/login и Password/password. Я сделал это для того, чтобы компилятор, да и мы сами, понимали различие внутренних переменных класса login и password от передаваемых переменных в конструкторе Login и Password.

107200


107201

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

Пример использование ключевго слова this в классе.:
public class Mail
{
    //У любого аккаунта есть логин и пароль

    public string login { get; set; }
    public string password { get; set; }

    public Mail(string login, string password)
    {
        this.login = login;
        this.password = password;
    }
}

/*
    Ключевое слово this помогает нам разрешить конфликт в названиях переменных,
    когда входящий параметр (в нашем случае - входящий параметр конструктора)
    назван так же, как внутренее поле(переменная) класса.
   
    Зачем это нужно?
    Мы ведь можем назвать входные параметры конструктора как угодно?
    Хоть "asdasd", хоть "login1", хоть "loginDlyaConstructora". Попробуйте оценить такие
    названия хотя бы визуально - выглядит как будто код писал начинающий программист в 5м классе.
    Переменные должны быть названы так, чтобы человек без опыта взглянул на них и сразу понял,
    зачем они нужны, не говоря уже о опытном программисте, который за 10 лет привык придерживаться
    четких правил в обозначении переменных.
*/

Как объекты могут взаимодействовать с другим кодом программы или друг с другом?

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

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

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

На любом собеседовании на позицию junior разработчика вас спросят про эти принципы. Существует 3 принципа ООП: инкапсуляция, наследование, полиморфизм. Не так давно в эту группу стали выделять 4й принцип - абстракция. Вам пока не стоит задумываться о том, 3 принципа или 4, лучше мы рассмотрим примеры.

Инкапсуляция. Что это такое? Примеры
Начнем с понятия этого непонятного слова "Инкапсуляция". Переводим слово на англ. транслит и получаем фразу "in capsule", что переводится как "в капсуле". В итоге имеет, что мы что-то положили в капсулу, отгородив это от чего-то другого - это и есть основа инкапсуляции.

Инкапсуляция - это принцип ООП при котором разработчик скрывает внутреннюю реализацию класса и его методов в самом классе.

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

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

Давайте теперь рассмотрим пример инкапсуляции в программе. Для этого нам пригодится наш ранее созданный класс Mail. Все примеры я связываю с реальной разработкой ботов для рассылок (т.к. это моя ниша) и упрощаю их для вашего понимая.

Напишем примерную абстрактную модель поведения аккаунта при отправке письма:
1) Авторизовались с помощью логина и пароля
2) Если аворизация прошла успешно, то нужно спарсить все необходимые куки для наших запросов
3) Если мы удачно спарсили куки, то можно отправить письмо. Для письма нам нужны текст и тема.

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

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

Пример реализации класса Mail (общий код):
//Создаём класс Mail

public class Mail
{
    //Cоздадим поля(переменные) класса и конструктор
   
    public string login { get; set; }
    public string password { get; set; }

    public Mail(string login, string password)
    {
        this.login = login;
        this.password = password;
    }

    /*
        Создадим 2 переменные класса: authResult и getCookiesResult
       
        authResult - будет хранить в себе результат авторизации.
                     Если true - аккаунт авторизовался успешно;
                     Если false - аккаунт не смог авторизоваться
                     (например из-за ввода неправильного логина или пароля).
           
        getCookiesResult - будет хранить в себе результат парсинга куков.
       
        Изначально эти переменные равняются false и принимают значение true
        только по ходу выпонения кода (после авторизации и получения куков соответственно)  
    */

    private bool authResult = false;
    private bool getCookiesResult = false;


    /*
        Создадим метод Auth, который будет отвечать за авторизацию аккаунта. На вход он
        примимает 2 параметра - логин и пароль.
       
        Если логин и пароль совпадают с теми значениями, которыя я, для примера,
        задал изначально "[email protected]" и "petr123", то авторизация считается
        успешной и переменной authResult присваивается значение true.
       
        Данная функция ничего и никуда не передаёт, поэтому она имеет тип void. Она
        просто переопределяет переменную authResult в засисимости от успеха авторизации.
    */

    private void Auth(string login, string password)
    {
        if(login == "[email protected]" && password == "petr123")
            authResult = true;
        else
            authResult = false;
    }

    /*
        Создадим метод Getcookies, который будет отвечать за получение куков.
       
        В нашем упращенном примере мы принимает тот факт, что для получения
        куков достаточно успешной авторизации на аккаунте. На реальных примерах
        это действительно является одним из основных условий получения куков. Вы
        не сможете получить нужных куков, если не авторизовались на аккаунте.
       
        Данный метод проверяет переменную authResult на значение true или false.
        Если значение true - то получение куков считается успешным и переменная
        getCookiesResult принимает значение true, иначе - значение false.
       
        Данная функция ничего и никуда не передаёт, поэтому она имеет тип void. Она
        просто переопределяет переменную getCookiesResult в засисимости от успеха
        получения куков.
    */

    private void Getcookies()
    {
        if(authResult == true)
            getCookiesResult = true;
        else
            getCookiesResult = false;
    }

    /*
        Создадим метод SendMsg, который будет отвечать за отправку письма. На вход он
        примимает 2 параметра - текст и тему.
       
        Для отправки письма нам нужно авторизоваться и получить куки. Для это, в данном
        методе мы обращаемся к ранее созданым методам Auth и Getcookies.
       
        Для финального действия, т.е. для отправки письма, мы проверяем, что куки были
        успешно получены, т.е. переменная getCookiesResult должна быть true. Также мы проверяем,
        что текст и тема не являются пустыми (зачем нам отправлять пустое сообщение?).
       
        Если все условия соблюдены, то сообщение считается отправленым и мы получаем результат
        "Успешно отправили письмо", иначе - получем результат "Не смогли отправить письмо".
    */

    public string SendMsg(string text, string title)
    {
        Auth(login, password);

        Getcookies();

        if(getCookiesResult == true && text != "" && title != "")
            return "Успешно отправили письмо";
        else
            return "Не смогли отправить письмо";
    }
}
Отправка письма с помощью класса Mail (c# кубик):
/*
    Создаём объект с названием mailAccount класса Mail.
    В качестве логина и пароля передаём заведомо правильные логин и пароль.
*/
Mail mailAccount = new Mail("[email protected]", "petr123");


/*
    Отправляем сообщение с аккаунта(объекта) mailAccount с помощю метода SendMsg.
    В качестве текста и темы передаём набор букв "asd" и "zxc".
*/
return mailAccount.SendMsg("asd", "zxc");

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

107205


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

Пример отправки письма с ошибками с помощью класса Mail (c# кубик):
Mail mailAccount1 = new Mail("[email protected]", "petr123456"); //специально указали неверный пароль для аккаунта mailAccount1
Mail mailAccount2 = new Mail("[email protected]", "petr123");

string result1 = mailAccount1.SendMsg("asd", "zxc");
string result2 = mailAccount2.SendMsg("asd", "");      //специально сделали тему письма пустой для аккаунта mailAccount2

project.SendInfoToLog("Результат отправки аккаунта1: " + result1);
project.SendInfoToLog("Результат отправки аккаунта2: " + result2);

Как результат - оба аккаунта не смогли отправить письмо:

107207

Модификаторы доступа
Внимательные читатели обратили внимание, что у классов, полей и методов я пишу приставку public и private - что это такое? Это называется модификатором доступа. Модификаторы доступа идут бок о бок с инкапсуляцией.

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

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

107209

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

Это в нашем упрощенном примере всего 3 метода, которые выполняют простейшие действия. А теперь представьте ситуацию, когда таких методов будет десяток, да еще и со сложной логикой внутри. Зачем программисту в этом капаться? - Правильно, не за чем.

На скрине показан хороший пример того, как работают модификаторы доступа. Нам доступны только те методы, которые имеют модификатор public.

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

Наследование. Что это такое? Примеры
Наследование - что же это такое? Понять данный принцип ООП достаточно просто, т.к. банально что-то у кого-то наследуется, прямо как в обычной жизни. Вспоминаем про автомобили. Для примера рассмотрим самый большой концерт - Volkswagen, а именно - его самый популярный автомобиль Volkswagen Golf.

Как вы думаете, сильно ли меняется автомобиль от модели к модели? Инженеры каждый раз создают новый автомобиль, или же пользуются старыми наработками? - Ответ очевиден, они пользуются предыдущим опытом.

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

В итоге - автомобиль наследует основные параметры и функции, но имеет и свои особенности от модели к модели.

Теперь посмотрим как происходит наследование в c# и других си-подобных языках.

Наследование (inheritance)
является одним из ключевых принципов ООП. Благодаря наследованию один класс может унаследовать функциональность другого класса.

Для примера вспоминаем 3 популярных почтовика: mail, gmail и yandex. У всех из них есть схожие функции, такие как отправка письма или чтение входящих сообщений.

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

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

Для начала я возьму весь код для реализации класса Mail, который был в инкапсуляции. Единственное, что я сделаю - это сменю название у класса (и конструктора) на EmailService - это будет класс-родитель. Как мы помним, в нём полностью реализован функционал отправки письма.

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

Пример использования Наследования (общий код):
/*
    Создаём класс EmailService, который на 100%
    идентичен классу Mail из инкапсуляции. Изменили
    только название класса и конструктора
*/

public class EmailService
{
    /*-----------------------------------------------------*/

    public string login { get; set; }
    public string password { get; set; }

    public EmailService(string login, string password)
    {
        this.login = login;
        this.password = password;
    }

    /*-----------------------------------------------------*/

    private bool authResult;
    private bool getCookiesResult;

    /*-----------------------------------------------------*/

    private void Auth(string login, string password)
    {
        if(login == "[email protected]" && password == "petr123")
            authResult = true;
        else
            authResult = false;
    }

    /*-----------------------------------------------------*/

    private void Getcookies()
    {
        if(authResult == true)
            getCookiesResult = true;
        else
            getCookiesResult = false;
    }

    /*-----------------------------------------------------*/

    public string SendMsg(string text, string title)
    {
        Auth(login, password);

        Getcookies();

        if(getCookiesResult == true && text != "" && title != "")
        {
            return "Успешно отправили письмо";
        }
        else
            return "Не смогли отправить письмо";
    }
}

/*
    Создаём класс Gmail и делаем его наследником класса EmailService через двоиточие.
    Не забываем прописать и конструктор.
*/

public class Gmail: EmailService
{
    public Gmail(string login, string password) : base(login, password)
    {
    }
}


/*
    Создаём класс Yandex и делаем его наследником класса EmailService через двоиточие.
    Не забываем прописать и конструктор.
*/

public class Yandex: EmailService
{
    public Yandex(string login, string password) : base(login, password)
    {
    }
}


/*
    Создаём класс Mail и делаем его наследником класса EmailService через двоиточие.
    Не забываем прописать и конструктор.
*/

public class Mail: EmailService
{
    public Mail(string login, string password) : base(login, password)
    {
    }
}


/*
    Ключевое слово base у конструкторов классов-наследников нужно для того,
    чтобы мы могли воспользоваться конструктором класса-родителя, т.к. в нём
    уже есть вся необходимая логика.
   
    Другими словами - мы переопределили конструкторы классов-наследников и сослались
    на конструктор класса-родителя.
   
    Мы могли бы сделать и обычную реализацию конструктора у классов-наследников, например:
   
    public class Yandex: EmailService
    {
        public Yandex(string login, string password)
        {
            this.login = login;
            this.password = password;
        }
    }
   
    Но мы вообще-то пытаемся избавиться от дублирования кода!)
    Осбенно, когда такого кода не 2 строчки, а 200, как в реальных примерах.
*/

Зачем мы проделали все эти действия? А вот зачем. Теперь мы можем делать отправку письма с любого аккаунта, будь-то Mail, Gmail или yandex. Мы добились это с помощью Наследования. Мы реализовали всю логику отпраки письма в классе-родителе

Отправка письма с аккаунтов Mail, Gmail и Yandex благодаря Наследованию (c# кубик):
//Создали аккаунт mail на базе родительсого класса EmailService
EmailService mail = new Mail("[email protected]", "petr123");

//Создали аккаунт gmail на базе родительсого класса EmailService
EmailService gmail = new Gmail("[email protected]", "petr123");

//Создали аккаунт yandex на базе родительсого класса EmailService
EmailService yandex = new Yandex("[email protected]", "petr123");

/*
    Т.к. мы унаследовали классы Mail, Gmail и Yandex от EmailService - нам
    становится доступен весь функционал класса EmailService, включая авторизацию (Auth),
    получение куков (getCookiesResult) и самое главное - отправка письма (SendMsg).

    Класс EmailService является родителельским классом.

    Классы Mail, Gmail и Yandex являются дочерними класами-наследниками.
*/


//Сделали отправку с аккаунта mail и сохранили результат отправки в переменную mailSendResult
string mailSendResult = mail.SendMsg("asd", "zxc");

//Сделали отправку с аккаунта gmail и сохранили результат отправки в переменную gmailSendResult
string gmailSendResult = gmail.SendMsg("asd", "zxc");

//Сделали отправку с аккаунта yandex и сохранили результат отправки в переменную yandexSendResult
string yandexSendResult = yandex.SendMsg("asd", "zxc");


//Выводим результат отправок в ЛОГ
project.SendInfoToLog("Результат отправки с mail: " + mailSendResult);

project.SendInfoToLog("Результат отправки с gmail: " + gmailSendResult);

project.SendInfoToLog("Результат отправки с yandex: " + yandexSendResult);


/*
    В этом примере я прошу опустить тот факт, что у акаунтов mail, gmail и yandex
    один и тот же логин и пароль, да еще и у логина домен - @mail.ru.
   
    Понятное дело, что это можно обыграть, но тогда бы это заструднило процесс объяснения.
*/
Как итог - успешная отправка со всех трёх почтовиков:
107217


Давайте чуть-чуть добавим реализма в наш код. Мы прекрасно понимаем, что 3 данных почтовика не являются идентичными на 100%. Например у аккаунта gmail есть доступ к ютубу, у аккаунта yandex есть доступ к дзену, а у аккаунта mail есть доступ к Моему Миру.

Реализуем это у классов-наследников в виде кода:
Пример собственных методов у классов-наследников (часть общего кода):
public class Gmail: EmailService
{
    public Gmail(string login, string password) : base(login, password)
    {
    }
   
    //Добавили метод для создания ютуб-аккаунта у Gmail
    public string CreateYoutubeAccount()
    {
        return "Успешно создали youtube-аккаунт";
    }
}


public class Yandex: EmailService
{
    public Yandex(string login, string password) : base(login, password)
    {
    }

    //Добавили метод для создания дзен-аккаунта у Yandex
    public string CreateDzenAccount()
    {
        return "Успешно создали дзен-аккаунт";
    }
}


public class Mail: EmailService
{
    public Mail(string login, string password) : base(login, password)
    {
    }

    //Добавили метод для создания моего мира у Mail
    public string CreateMyWold()
    {
        return "Успешно создали мой мир";
    }
}
В качестве примера воспользуемся собсвенным методом для создания Моего Мира у класса Mail:

107222


107223

Вывод по Наследованию
Принцип наследование позволяет нам выделить общие признаки и реализовать их в отдельной сущности (класс-родитель). Простыми словами - мы можем вынести за скобку общий функционал, который будет един для всех классов-наследников. Еще проще - мы избавляемся от дублирования кода, переиспользуя код класса-родителя.

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


Полиморфизм. Что это такое? Примеры
Полиморфизм - что же это такое? Давайте для начала, как обычно, поймем состав данного слова. Слово состоит из двух друг слов: poly и morfling(вместо этого слова можно подобрать другие)

Приставка poly - означает, что в составе объекта, к которому добавляется эта приставка, есть какая-то разновидность. Например: полиглот - человек, который знает несколько языков; Полимер - вещество, состоящие из нескольких других веществ (мономеров) и т.п.

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

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

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

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

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

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

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

Пример полиморфизма (общий код):
public abstract class EmailService
{
    /*-----------------------------------------------------*/
   
    public string login { get; set; }
    public string password { get; set; }
   
    public EmailService(string login, string password)
    {
         this.login = login;
        this.password = password;
    }
   
    /*-----------------------------------------------------*/
   
    private bool authResult;
    private bool getCookiesResult;

    /*-----------------------------------------------------*/
   
    private void Auth(string login, string password)
    {
        if(login == "[email protected]" && password == "petr123")
            authResult = true;
        else
            authResult = false;
    }
   
    /*-----------------------------------------------------*/
   
    private void Getcookies()
    {
        if(authResult == true)
            getCookiesResult = true;
        else
            getCookiesResult = false;
    }
   
    /*
        Создаём обстрактный метод SendMsg() с помощью ключевого слова abstract.
        У данного метода нет никакой реализации, поэтому после объявления метода
        просто ставим точку с запятой ";"
       
        Он называется абстрактным, т.к. он не является чем-то конктретным. У него даже нет
        входящих параметров. Он просто существует, как какое-то аморфное существо.
       
        Мы это сделали, чтобы у нас была возможность сделать несколько вариантов реализации
        данного метода (poly, помните?)
    */
   
    public abstract string SendMsg();
   
   
    /*
        Создали дополнительный метод CreoValidation, который проверяет текст и тему на пустоту.
       
        Это никак не относится к полиморфизму. Просто я захотел для такой проверки
        внаписать специальный метод.
       
        Если текст или тема пустая - результатом будет false.
        Если текст и тема не пустые - результатом будет true.
    */
   
    private bool CreoValidation(string text, string title)
    {
        if(text != "" && title != "")
            return true;
        else
            return false;
    }
   
    /*
        1ая реализация метода SendMsg, при котором на вход поступают
        только текст и тема, но нет инфрмации о том, кому нужно отправить письмо.
       
        В реальном проекте такое может означать, что письмо нужно отправить самому себе.
       
        Каждая такая реализация называется "Перегрузкой" метода. Можно сказать, что это
        полиморфизм на уровне методов - каждый раз будет разный набор входных данных.
    */
   
    public string SendMsg(string text, string title)
    {
        if(CreoValidation(text, title))
        {
            return SendMsg(); //Это что еще такое? Один SendMsg() внутри другого SendMsg()? Ответ будет в классе-наследнике
        }
        else
            return "Не удалось отправить письмо";
    }
   
    /*
        2ая реализация метода SendMsg, при котором на вход поступают
        текст, тема и почта адресата.
       
        В реальном проекте такое может означать обычную отправку письма любому адресату.
    */
   
    public string SendMsg(string text, string title, string emailAddress)
    {
        if(CreoValidation(text, title))
        {
            return SendMsg(); //Это что еще такое? Один SendMsg() внутри другого SendMsg()? Ответ будет в классе-наследнике
        }
        else
            return "Не удалось отправить письмо";
    }
   
    /*
        3ая реализация метода SendMsg, при котором на вход поступают
        текст, тема, почта адресата, а также число отправок.
       
        В реальном проекте такое может означать отправку письма любому адресату, но несколько раз.
    */
   
    public string SendMsg(string text, string title, string emailAddress, int sendNumber)
    {
        if(CreoValidation(text, title))
        {
            return SendMsg(); //Это что еще такое? Один SendMsg() внутри другого SendMsg()? Ответ будет в классе-наследнике
        }
        else
            return "Не удалось отправить письмо";
    }
}

public class Gmail: EmailService
{
    public Gmail(string login, string password) : base(login, password)
    {
    }
   
    public string CreateYoutubeAccount()
    {
        return "Успешно создали youtube-аккаунт";
    }
   

    /*
        А вот и ответ на загадку с SendMsg() внутри другого SendMsg().
       
        Что мы сделали? Мы сделали для класса-наследника Gmail свою собствтенную
        реализацию метода SendMsg(). В нашем упрощенном примере - это реализовано тем,
        что при успешной оправке будет написано, что письмо отправленно именно с Gmail.
       
        Мы можем сделать свою собственную реализацию метода SendMsg() для каждого
        класса-наследника. Это и есть проявление полиморфизма.
       
        Ключевое слово override означает, что мы написали конкретную реализацию
        абстрактного метода. Речь идет именно о том, что мы реализовали абстрактный
        метод класса-родителя в классе-наследнике.
    */
    public override string SendMsg()
    {
        return "Успешно отправили письмо c Gmail";
    }
}


public class Yandex: EmailService
{
    public Yandex(string login, string password) : base(login, password)
    {
    }
   
    public string CreateDzenAccount()
    {
        return "Успешно создали дзен-аккаунт";
    }
   
    /*
        А вот и ответ на загадку с SendMsg() внутри другого SendMsg().
       
        Что мы сделали? Мы сделали для класса-наследника Yandex свою собствтенную
        реализацию метода SendMsg(). В нашем упрощенном примере - это реализовано тем,
        что при успешной оправке будет написано, что письмо отправленно именно с Yandex.
    */
    public override string SendMsg()
    {
        return "Успешно отправили письмо c Yandex";
    }
}


public class Mail: EmailService
{
    public Mail(string login, string password) : base(login, password)
    {
    }
   
    public string CreateMyWold()
    {
        return "Успешно создали мой мир";
    }
   
    /*
        А вот и ответ на загадку с SendMsg() внутри другого SendMsg().
       
        Что мы сделали? Мы сделали для класса-наследника Mail свою собствтенную
        реализацию метода SendMsg(). В нашем упрощенном примере - это реализовано тем,
        что при успешной оправке будет написано, что письмо отправленно именно с Mail.
    */
    public override string SendMsg()
    {
        return "Успешно отправили письмо c Mail";
    }
}

Отправка письма с разных почтовиков, используя один и тот же метод SendMsg (c# кубик):
EmailService gmail = new Gmail("[email protected]", "petr123"); //Создали аккаунт (объект) Gmail на основе родительского класса EmailService
project.SendInfoToLog(gmail.SendMsg("asd", "zxc"));        //Отправили сообщение с аккаунта gmail

EmailService mail = new Mail("[email protected]", "petr123");   //Создали аккаунт (объект) Mail на основе родительского класса EmailService
project.SendInfoToLog(mail.SendMsg("asd", "zxc"));         //Отправили сообщение с аккаунта mail

EmailService yandex = new Yandex("[email protected]", "petr123"); //Создали аккаунт (объект) Yandex на основе родительского класса EmailService
project.SendInfoToLog(yandex.SendMsg("asd", "zxc"));         //Отправили сообщение с аккаунта yandex

Мы создали 3 аккаунта для разных почтовиков и у каждого вызвали один и тот же метод SendMsg(). Как итог - для каждого аккаунта (т.е. для каждого класса-наследника) отработала своя рализация метода SendMsg().

107228

Не забывем, что у нас есть разная реализация не только на уровне класса-наследников, но и на уровне самого метода (когда на вход поступают разные данные)

Использование одного и тогоже метода, но с разным набором входных данных (c# кубик):
EmailService gmail = new Gmail("[email protected]", "petr123"); //Создали аккаунт (объект) Gmail на основе родительского класса EmailService

project.SendInfoToLog(gmail.SendMsg("asd", "zxc"));    //Отправили сообщение с аккаунта gmail, используя только текст и тему

project.SendInfoToLog(gmail.SendMsg("asd", "zxc", "[email protected]")); //Отправили сообщение с аккаунта gmail, используя текст, тему и адресата

project.SendInfoToLog(gmail.SendMsg("asd", "zxc", "[email protected]", 5)); //Отправили сообщение с аккаунта gmail, используя текст, тему, адресатаи и число отправок
Как итог - все попытки отправить письмо с разным набором входных данных увенчались успехом:

107229

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

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

Представьте ситуацию, если бы мы писали софт не для 3их почтовиков, а для 30 почтовиков. Самое удобное название для метода отправки письма - это SendMsg() и я не хочу менять это название 30 раз. Я не хочу дежать в голове 30 названий методов, которые призваны для одного и того же.

Вполне вероятно, что в вашей программе будет менее очевидный по функционалу метод, например PrintResult(), который должен выводить в ЛОГ какой-то результат. Представьте ситуацию, что у вас 10 классов и в каждом есть необходимость выводить результат каких-то действий в ЛОГ. Самым верным путем будет реализация метода PrintResult() по принципу полиморфизма - в каждом классе своя реализция. Не нужно будет держать в голове 10 названий методов по выводу информации в ЛОГ.

Общий итог
1. В основе ООП лежат классы и объекты. Класс - это шаблон (или модель) по которой создаются объекты (экземпляры) класса. Класс является типом данных. Если вы создали объект класса Mail, то и тип данных у объекта будет Mail (не int, не string и т.п.)

2. Для определения переменных (полей) во время создания объектов - в классе необходимо сделать конструктор. Название конструктора такоеже, как название класса.

3. Чтобы непутать переменые (поля) класса с одноименными входными переменными конструктора нужно использовать ключевое слово this.

4. Существует 3 основных принципа ООП - инкапсуляция, наследование и полиморфизм.

5. Инкапсуляция - это принцип ООП, при котором принятно скрывать внутреннюю реализацю класса, а также его методов и внутренних переменных. Делается это с помощью правильного написания кода и модификаторов доступа.

6. Наследование - это принцип ООП, при котором принято переиспользовать код, чтобы его не дублировать. Сущесвуют классы-родители и классы-наследники. Классы-наследники могут унаследовать реализацию кода от класса-родителя.

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

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


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

Для лучшего изучения я прикрепил к статье 3 шаблона (по одному на каждый принцип ООП). Минимальная верся зено 7.2

Мои контакты:
• Мой телеграм: https://t.me/brabus_soft
• Мой блог о рассылках: https://t.me/brabus_soft_channel

Спасибо за внимание!
 
Номер конкурса статей
Девятнадцатый конкурс статей

Вложения

Для запуска проектов требуется программа ZennoPoster или ZennoDroid.
Это основное приложение, предназначенное для выполнения автоматизированных шаблонов действий (ботов).
Подробнее...

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

Последнее редактирование модератором:

socsecret.ru

Client
Регистрация
30.09.2017
Сообщения
343
Благодарностей
207
Баллы
43
Спасибо, C# вещь нужная в Зенке.
 
  • Спасибо
Реакции: Brabus_bots

Sherminator

Client
Регистрация
10.09.2021
Сообщения
996
Благодарностей
535
Баллы
93
Надо было между базой и ООП объяснить как визуал студией пользоваться ) а то от ООП, когда у тебя 30 классов одной стеной кода в общем коде идет, мало пользы)
 
  • Спасибо
Реакции: SHILY и Dmitriy Ka

Brabus_bots

Client
Регистрация
13.04.2019
Сообщения
656
Благодарностей
434
Баллы
63
Надо было между базой и ООП объяснить как визуал студией пользоваться ) а то от ООП, когда у тебя 30 классов одной стеной кода в общем коде идет, мало пользы)
30 классов - это вы так называете 4 класса в моих примерах?)

Я считаю, что легче воспринимать информацию в знакомой среде, т.е. в Zennoposter. Интерфейс вижака(VS) может банально запутать не знающего человека.

Около-большие проекты, естественно, лучше писать и отлаживать в VS. Для простеньких примеров хватит зенопостера.

Как задел для будущей статьи - идея хорошая.
 
  • Спасибо
Реакции: Sherminator

Sherminator

Client
Регистрация
10.09.2021
Сообщения
996
Благодарностей
535
Баллы
93
30 классов - это вы так называете 4 класса в моих примерах?)

Я считаю, что легче воспринимать информацию в знакомой среде, т.е. в Zennoposter. Интерфейс вижака(VS) может банально запутать не знающего человека.

Около-большие проекты, естественно, лучше писать и отлаживать в VS. Для простеньких примеров хватит зенопостера.

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

Brabus_bots

Client
Регистрация
13.04.2019
Сообщения
656
Благодарностей
434
Баллы
63
Не, я не про примеры и не про статью, а про то что в самом интнрфейсе зенопостера ООП применять не очень удобно и эффективно
Есть такое) Но что имеем, то имеем. Главное, чтобы читателям был понятен материал. Я специально делал все примеры очень ёмкими, чтобы они поместились в небольшие шаблоны на зенке.

Быстро скачать, открыть в PM, запустить, посмотреть - профит

Следующую статью можно как раз посвятить фичам полноценной среды для разработки. А можем кто-то и до меня это успеет сделать
 

Sherminator

Client
Регистрация
10.09.2021
Сообщения
996
Благодарностей
535
Баллы
93
Есть такое) Но что имеем, то имеем. Главное, чтобы читателям был понятен материал. Я специально делал все примеры очень ёмкими, чтобы они поместились в небольшие шаблоны на зенке.

Быстро скачать, открыть в PM, запустить, посмотреть - профит

Следующую статью можно как раз посвятить фичам полноценной среды для разработки. А можем кто-то и до меня это успеет сделать
Это да) работаем с тем что имеем ) Тоже думал про статью связанную с использованием vs, но там столько рассказывать надо) в том числе и про ООП , в итоге поленился
 
  • Спасибо
Реакции: Brabus_bots

Oleg1987

Client
Регистрация
11.08.2014
Сообщения
1 165
Благодарностей
744
Баллы
113
Я считаю, что легче воспринимать информацию в знакомой среде, т.е. в Zennoposter. Интерфейс вижака(VS) может банально запутать не знающего человека.
Это да. Но проблема в том, что PM априори не позволяет в ООП стили писать проект. Только процедурный подход. То что вы классов на создавали - это не ООП.
 
  • Спасибо
Реакции: Zedx, djaga и Astraport

Sherminator

Client
Регистрация
10.09.2021
Сообщения
996
Благодарностей
535
Баллы
93
Это да. Но проблема в том, что PM априори не позволяет в ООП стили писать проект. Только процедурный подход. То что вы классов на создавали - это не ООП.
Так надо проект запустить, и классы станут объектами ) чем не ооп
 
  • Спасибо
Реакции: Brabus_bots

Brabus_bots

Client
Регистрация
13.04.2019
Сообщения
656
Благодарностей
434
Баллы
63
Это да. Но проблема в том, что PM априори не позволяет в ООП стили писать проект. Только процедурный подход. То что вы классов на создавали - это не ООП.
Так и не понял, о чем вы, увы. На моих примерах вы не поняли, что такое инкапсуляци, наследование и полиморфизм? Или не поняли, где создание объектов происходит? Или что именно вы не поняли?

P.S. Я спросил серьезно)
 
Последнее редактирование:

Oleg1987

Client
Регистрация
11.08.2014
Сообщения
1 165
Благодарностей
744
Баллы
113

Sherminator

Client
Регистрация
10.09.2021
Сообщения
996
Благодарностей
535
Баллы
93
Ok. Я создал объект класса Gmail. А как теперь мне им воспользоваться в разных местах проекта?
Так класс прописывается в общем коде, а в с# сниппите ты уже создаешь объект, а если надо что бы экземпляр один был, то синглтон делаешь и будешь работать с одним объектом в разных c# сниппетах
 

Oleg1987

Client
Регистрация
11.08.2014
Сообщения
1 165
Благодарностей
744
Баллы
113

Sherminator

Client
Регистрация
10.09.2021
Сообщения
996
Благодарностей
535
Баллы
93

radv

Client
Регистрация
11.05.2015
Сообщения
3 678
Благодарностей
1 860
Баллы
113
Интерфейс вижака(VS) может банально запутать не знающего человека.
Его можно настроить под себя. и тут вопрос привычки к интерфейсу и потребностей в функционале. Я например часто используемые действия вынес на кнопки отдельной панели меню для удобства и скорости работы. Visual Studio Code вообще с минимальным интерфейсом, который сильно отличается от VS Community. И перейти с одного на другое будет сложно и неудобно. VS Code вообще нужно настраивать отдельно, под каждый язык (например кроме шарпа если хотите изучать питон или что то еще). У каждой есть свои плюсы и минусы, так что было бы желание научиться. :az:
 
  • Спасибо
Реакции: Brabus_bots

radv

Client
Регистрация
11.05.2015
Сообщения
3 678
Благодарностей
1 860
Баллы
113
Ok. Я создал объект класса Gmail. А как теперь мне им воспользоваться в разных местах проекта?
нажимаешь точку после названия объекта и у тебя появляется список публичных доступных объектов этого класса с которыми можно работать.
 
  • Спасибо
Реакции: Brabus_bots

Astraport

Client
Регистрация
01.05.2015
Сообщения
4 943
Благодарностей
4 336
Баллы
113
Для меня ООП это прежде всего паттерны типа MVVM, MVC. Это реализуемо в ЗП?
 

Brabus_bots

Client
Регистрация
13.04.2019
Сообщения
656
Благодарностей
434
Баллы
63
Для меня ООП это прежде всего паттерны типа MVVM, MVC. Это реализуемо в ЗП?
Если я правильно понял вопрос, то можно. Ничего не мешает получить данные с формы (входные настройки в ZP) и написать классы для их валидации/обработки и сохранения, чтобы это работало в рамках какого-то бота/шаблона.
 

fridayman

Client
Регистрация
25.03.2018
Сообщения
167
Благодарностей
259
Баллы
63
На мой взгляд, статья супер!
 
  • Спасибо
Реакции: Brabus_bots

Dmitriy Ka

Client
Регистрация
03.05.2016
Сообщения
541
Благодарностей
291
Баллы
63
Тема ООП очень обширная и непростая. Понимаю, что очень сложно ее описать одной статьей (даже не возможно :-) ).

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

Скажу честно, сам еще слабоват в понимании Инкапсуляции, Наследовании и Полиморфизме, но для новичков данная статья будет больше вредить, чем помогать освоить ООП.
 
  • Спасибо
Реакции: djaga

Brabus_bots

Client
Регистрация
13.04.2019
Сообщения
656
Благодарностей
434
Баллы
63
Тема ООП очень обширная и непростая. Понимаю, что очень сложно ее описать одной статьей (даже не возможно :-) ).

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

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

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

Пример инкапсуляции из статьи не становится ужасным лишь из-за того, что я не разделил 3 метода. Это в целом не сильно завязано на инкапсуляции, как таковой. Это можно назвать отдельным принципом ООП.

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

Но, т.к. замечание от @Vamp1k правильное - я дам короткое пояснение для читающих новичков.

Принцип одной ответственности
(он же принцип единственной обязанности, он же принцип единственной ответственности SRP) заключается в том, чтобы каждый класс и каждый в нём метод отвечал за конкретнную функцию.

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

В моё примере функция для отправки письма SendMsg() завязана на 2х других методах - для авторизации Auth() и для получении куков Getcookies(). В реальности так делать не желательно и нужно разделять такие методы, чтобы каждый метод делал только изначально возложенную на него задачу.

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

Если бы в моём примере почта адресата запрашивалась из БД, то правильным решением было бы разграничить работу с БД и отправку письма разными классами - не нужно всё городить в одном месте.

Это не единственный плюс такого подхода. Как еще один пример - переиспользование кода становится куда проще, когда между блоками кода нет каких-то, на первый взляд, скрытых зависимостей.
 
Последнее редактирование:
  • Спасибо
Реакции: gelu4, Dmitriy Ka и djaga

Porosenok

Client
Регистрация
26.09.2010
Сообщения
1 279
Благодарностей
96
Баллы
48
Подскажите про полиморфизм и SendMsg, когда его через override определяем в классах-наследниках, по факту тогда тот код что в методе SendMsg в абстрактном классе тоже будет выполняться при вызове этого метода? Или если мы сделали override то по факту наш метод будет выполнять только код
return "Успешно отправили письмо c Mail/Yandex/Gmail";
а то что в абстрактном классе выполняться не будет?
 
  • Спасибо
Реакции: Brabus_bots

Brabus_bots

Client
Регистрация
13.04.2019
Сообщения
656
Благодарностей
434
Баллы
63
Подскажите про полиморфизм и SendMsg, когда его через override определяем в классах-наследниках, по факту тогда тот код что в методе SendMsg в абстрактном классе тоже будет выполняться при вызове этого метода? Или если мы сделали override то по факту наш метод будет выполнять только код
return "Успешно отправили письмо c Mail/Yandex/Gmail";
а то что в абстрактном классе выполняться не будет?
В абстрактном (перегруженном) методе SendMsg() вызывается переопределённый метод override SendMsg() из классов наследников. Другими словами - выполняется код из классов-наследников.

UPD
Если в перегруженном абстрактном методе SendMsg() будет какой-то код, то он тоже выполнится.
 
Последнее редактирование:

lzlmrf

Client
Регистрация
14.08.2015
Сообщения
487
Благодарностей
148
Баллы
43
Спасибо. Хороший и понятный слог, все доходчиво.
 
  • Спасибо
Реакции: Brabus_bots

Porosenok

Client
Регистрация
26.09.2010
Сообщения
1 279
Благодарностей
96
Баллы
48
В абстрактном (перегруженном) методе SendMsg() вызывается переопределённый метод override SendMsg() из классов наследников. Другими словами - выполняется код из классов-наследников.

UPD
Если в перегруженном абстрактном методе SendMsg() будет какой-то код, то он тоже выполнится.
то есть если я вызову в коде метод SendMsg() класса Gmail то выполнится не просто код return "Успешно отправили письмо c Gmail";
Но сначала код этого метода из абстрактного класса, а потом уже когда в этом коде этот метод повторится то тогда выполнится и return "Успешно отправили письмо c Gmail";?

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

Nats1

Client
Регистрация
15.04.2015
Сообщения
197
Благодарностей
194
Баллы
43
Зачем это все если есть chat gpt 4? И 100000% что это будет и дальше развиваться.
 
Последнее редактирование:

Brabus_bots

Client
Регистрация
13.04.2019
Сообщения
656
Благодарностей
434
Баллы
63
то есть если я вызову в коде метод SendMsg() класса Gmail то выполнится не просто код return "Успешно отправили письмо c Gmail";
Но сначала код этого метода из абстрактного класса, а потом уже когда в этом коде этот метод повторится то тогда выполнится и return "Успешно отправили письмо c Gmail";?

Просто тут вот написано что будет при вызове класса наследника будет выполняться тот метод который override и все, без кода из основного класса
Предлагаю Вам сделать быстрый тест) В родительском классе в каждой реализации(перегрузке) метода SendMsg() есть поверка текста и темы на пустоту. Давайте воспользуемся этим и попробуем сделать отправку письма с пустой темой или текстом.

Если у объектов-наследников выполняется только метод override SendMsg(), то проверка на пустоту текста и темы просто проигнорируется и мы увидим в ЛОГе "Сообщение успешно отправлено".

Пример отпправки с пустой темой:
EmailService gmail = new Gmail("[email protected]", "petr123"); //Создали аккаунт (объект) Gmail на основе родительского класса EmailService

project.SendInfoToLog(gmail.SendMsg("asd", ""));    //Отправили сообщение с аккаунта gmail, но с пустой темой

Результат:

107544
 

Brabus_bots

Client
Регистрация
13.04.2019
Сообщения
656
Благодарностей
434
Баллы
63
Зачем это все есть есть chat gpt 4? И 100000% что это будет и дальше развиваться.
Ну да, Вы правы. Передам всем программистам, чтобы искали себе новую работу. Разработчикам зенопостера тоже сообщу плохую для них новость :-)

UPD
Шутка, естественно.
 
  • Спасибо
Реакции: n0n3mi1y

Nats1

Client
Регистрация
15.04.2015
Сообщения
197
Благодарностей
194
Баллы
43
Ну да, Вы правы. Передам всем программистам, чтобы искали себе новую работу. Разработчикам зенопостера тоже сообщу плохую для них новость :-)

UPD
Шутка, естественно.
Начните с него в плане передачи приветов https://forklog.com/news/ai/bill-gejts-predrek-smert-google-i-amazon-iz-za-ii он его ждёт от Вас.

А ещё вставьте самый большой кусок кода что в статье в чат гпт и попросите его оптимизировать, не исключено что узнаете новое для себя.
 
Последнее редактирование:

Кто просматривает тему: (Всего: 1, Пользователи: 0, Гости: 1)