Русский
Русский
English
Статистика
Реклама

Шаблоны проектирования

Перевод Архитектор современных алгоритмов

05.04.2021 16:10:13 | Автор: admin

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

Барбара Лисков изобрела архитектуру, лежащую в основе современных программ. Создание чего-либо достаточно мощного это искусство.

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

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

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

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

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

Барбара Лисков

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

Когда она была еще молодым профессором Массачусетского технологического института, она возглавляла команду, создавшую первый язык программирования, который не полагался на операторы goto. Язык CLU (сокращение от cluster) основывался на изобретенном ею подходе абстракции данных который организовывал код в модули. Все используемые сегодня важные языки программирования, включая Java, C++ и C#, являются потомками CLU.

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

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

Вы достигли профессионального совершеннолетия во время разработки искусственного интеллекта. Как изменилось представление об искусственном интеллекте и машинном обучении за время вашей карьеры?

Я защитила докторскую диссертацию с Джоном Маккарти в области искусственного интеллекта. Я написала программу для разыгрывания шахматных эндшпилей. Эту тему предложил Джон, потому что я не играла в шахматы. Я прочитала [шахматные] учебники и перевела эти алгоритмы в поле информатики. В то время, вершиной научной мысли было сделать так, чтобы программа вела себя так, как поступал бы человек. Сейчас уже дела обстоят иначе.

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

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

Вас он больше интересовал как отдельная дисциплина?

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

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

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

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

Если бы у вас была волшебная палочка, и вы могли бы направить развитие информатики вперед, как бы это выглядело?

Я очень переживаю за интернет. У нас целая куча проблем, включая фейковые новости и проблемы с безопасностью. Меня беспокоит разведенная пара, в которой муж публикует клевету на жену, в том числе информацию о том, где она живет. Происходят ужасные вещи. Частично это выросло из настроений 80-х. В то время у нас было 15 университетов и пара государственных лабораторий, соединенных интернетом. Мы все были друзьями. Позиция заключалась в том, что сайты не должны нести ответственности за контент. Это задушит их развитие. Вы можете наблюдать, что такое отношение пока сохраняется.

Было ли такое мышление следствием академической свободы?

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

Видео: Барбара Лисков говорит о вызовах, с которыми сталкиваются компьютерные науки.

Расскажите мне о своем личном опыте в качестве женщины в информатике.

Меня поощряли хорошо учиться в школе. Не знаю, подбадривала ли меня мама, но она точно не мешала мне и не говорила: О нет, это плохая идея. Я прошла все курсы математики и естествознания, к чему девочек не поощряли. В Беркли я была одной из двух женщин (или даже единственной) в классе из 100 человек. Никто никогда не говорил: Вот это да, дела у тебя идут хорошо, почему бы тебе не поработать со мной? Я не знал, что такое вообще может происходить. Я поступила в аспирантуру Стэнфорда. Когда я закончила учебу, со мной никто не разговаривал о работе. Я заметил, что коллег-мужчин, таких как Радж Редди, который был моим другом, брали на академические должности. Мне такую работу никто не предлагал.

В то время научные руководители трудоустраивали выпускников по контрактам с ведомствами по всей стране.

Да, но мне никто ничего не предлагал. В 90-х я вернулась в Стэнфорд на день факультета. Группа старых профессоров, не осознавая, что они делают, рассуждали о подобном кумовстве (old boy network). Они говорили: О, мой друг сказал мне, что у меня есть хороший молодой парень, которого ты должен нанять. Так обстояли дела. Они не понимали, что тут не так. Они говорили о молодой женщине, которая так преуспела, потому что вышла замуж за профессора! Разве это правильно? У другого коллеги в офисе была пинап-фотография кинозвезды. Я спросил его: Что это за пинап-фотография в твоем офисе? Никакого вразумительного ответа.

Я подала заявление в Массачусетский технологический институт, но меня даже не рассматривали на должность преподавателя. Когда такое происходит, вы думаете: Я недостаточно хороша. Вы ничего не можете с этим поделать. Но также я думала: Информатика - открытая наука. Моя промышленная работа в Mitre была хорошей исследовательской работой. Там я работала над методологией программирования и проводила исследования, которые принесли мне мою первую премию. Затем в 1971 году я выступила с докладом, после которого Корби [Фернандо Корбато] пригласил меня в Массачусетский технологический институт. Меня также пригласили в Беркли. Ситуация менялась.

Тем не менее, правда ли, что когда вы начали работать в Массачусетском технологическом институте, было около 1000 преподавателей, из которых только 10 были женщинами?

Насколько я помню.

Так что прогресс был, но

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

В 2018 году Лисков была награждена премией Computer Pioneer Award в знак признания ее достижений.

Мне кажется, что ни одна научная область не признала какой-либо фундаментальный вклад женщин.

За 10 лет до того, как я возглавила факультет информатики в Массачусетском технологическом институте, департамент нашел только одну женщину достойной предложения работы. Когда я была руководителем [с 2001 по 2004 год], я наняла семь женщин. По сусекам скрести не приходилось. Все три молодые женщины, которых я наняла выдающиеся. Долгое время женщины вообще не рассматривались.

После того, как вы выиграли премию Тьюринга, в сети появился комментарий: Почему она получила эту награду? Она не сделала ничего, о чем мы еще не знали. Мог ли этот пренебрежительный комментарий иметь отношение к тому факту, что вы женщина.

Бьюсь об заклад, что да! Был еще один комментарий о котором я никогда не рассказываю в котором говорилось: О, это не она проделал эту работу. [Коллега-мужчина] сделал это вместо нее. Это полная чушь. Я не смотрела комментарии. Мой муж смотрел. Это была пара, о которых он мне поведал. Иногда я выступаю с докладами, на которых мне задают враждебные вопросы (и к этому нужно быть готовым), будь то из-за того, что я женщина, или потому, что люди пытаются сконфузить меня, понимаете

Сконфузить лауреата премии Тьюринга?

Ага! Тогда я не понимала, что некоторые люди в моем отделе поддерживают меня. И к тому времени, когда я начала путешествовать, я была уже хорошо известна. Но вот в чем загадка: почему некоторые женщины способны стойко, упорно продолжать двигаться вперед?

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

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

Это достаточно деликатно. В вашей истории обнаруживается скрытый подтекст: не высовывайся, пока не сможешь по-настоящему стоять прямо, а затем всецело отдайся этому.

Да, возможно, это была моя стратегия. Это, вместе с отсутствием потребности угождать другим людям. Женщины социализированы, чтобы угождать.

Это уже конкретный совет: откажитесь от потребности угождать.

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

Вы действительно так думаете? Ваш вклад изменил компьютерные технологии и общество.

Вы знаете, вы просто идете по этому извилистому пути, и кто знает, что будет?


Узнать подробнее о курсе Архитектура и шаблоны проектирования.

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

Подробнее..

Перевод Как я реализовал MVC в JavaScript

08.01.2021 02:22:10 | Автор: admin

для лучшей разделяемости кода

Для будущих студентов курса "Архитектура и шаблоны проектирования" и всех интересующихся подготовили перевод полезного материала.

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


Что из себя представляет архитектурный паттерн Model, View, Controller (MVC)?

Источник: документация Rails

Архитектура MVC разделяет ваш код на три (3) уровня: модели (Models), представления (Views) и контроллеры (Controllers), выполняющие различные задачи внутри программы.

Изображение взято из ВикипедииИзображение взято из Википедии

Уровень модели

В Ruby on Rails этот уровень содержит модель предметной области, которая обычно представляет определенный класс объектов (например, Человек, Животное, Книги). Обычно именно здесь обрабатывается бизнес-логика, поскольку модель связана с базой данных, и данные для нее извлекаются из строк соответствующей таблицы.

Уровень представления

Обрабатывает визуальное представление ответов, предоставляемых контроллерами. Поскольку контроллер может возвращать информацию в формате HTML, XML, JSON и т. д.

Уровень контроллера

В Rails этот уровень отвечает за взаимодействие с моделью, манипулирование ее данными и предоставление соответствующих ответов на различные HTTP-запросы.

Как бы паттерн MVC выглядел в JavaScript?

Источник: документация MDN

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

Изображение взято с MDNИзображение взято с MDN

Уровень модели

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

Возьмем, к примеру, приложение Classroom, которое отслеживает, какие классы посещает человек. В этом случае уровень модели можно разделить на классы, такие как Classroom, Person и модель на основе массива под названием Subjects.

Базовые классы модели

class Classroom {  constructor(id, subject = 'Homeroom') {    this.id = id;    this.persons = [];    this.subject = subject;  }}

Модель Classroom содержит переменные данных, которые будут содержать информацию по каждому классу. Сюда будут входить список всех людей, которые в настоящее время числятся в этом классе, предмет, связанный с этим классом, и его id.

class Person {  constructor(id, firstN = 'John', lastN = 'Doe') {    this.id = id;    this.firstName = firstN;    this.lastName = lastN;    this.subjects = [];    this.classrooms = [];  }}

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

const subjects = [  "English",  "Math",  "Computer Science",  "Business",  "Finance",  "Home Economics"];

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

Уровень контроллера

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

Например, в приложении Classroom контроллер получает данные, вводимые пользователем, от элементов представления, таких как ввод текста (text input) или выбор из списка опций (select options), а также нажатия кнопок, которые используются для изменения модели.

import classroomModel from "../models/classroom";class ClassroomController {  constructor() {    this.lastID = 0;    this.classrooms = [];    this.selectedClass = null;  }  selectClassroom(classroomID) {    this.selectedClass = this.classrooms    .filter(c => c.id === parseInt(classroomID, 10))[0];  }  addClassroom(subject) {    this.classrooms.push(      new classroomModel(this.lastID, subject)      );    this.lastID += 1;  }  removeClassroom(classroomID) {    this.classrooms = this.classrooms      .filter(c => c.id !== parseInt(classroomID, 10));  }  setSubject(subject, classroomID) {    const classroom = this.classrooms      .filter(c => c.id === parseInt(classroomID, 10))[0];    classroom.subject = subject;  }  addPerson(person, classroom) {    // const classroom = this.classrooms    // .filter(c => c.id === parseInt(classroomID, 10))[0];    if (!person) return;    classroom.addPerson(person);  }  removePerson(person, classroomID) {    const classroom = this.classrooms    .filter(c => c.id === parseInt(classroomID, 10))[0];    classroom.removePerson(person);  }}

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

Этот контроллер имеет три собственные переменные: lastID (каждый раз, когда объект класса создается и добавляется к массиву классов, значение этой переменной инкрементируется), classrooms (массив всех созданных объектов класса) и selectedClass.

Уровень представления

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

Например, в приложении Classroom представление будет предоставлять элементы DOM (объектной модели документа), такие как кнопки, инпуты и контейнеры (<div/>, <span/ >, <p/> и т. д.) для отображения различных людей и классов, и связанных с ними данных.

import classroomController from "../controllers/classroom";import subjects from "../models/subjects";class ClassroomView {  constructor(appDiv) {    this.classroomController = new classroomController();    this.classroomSectionDiv = document.createElement('div');    this.classroomsDiv = document.createElement('div');    this.addclassBtn = document.createElement('button');    this.selectSubjectInput = document.createElement('select');    this.classroomSectionDiv.classList.add('classroom-section');    this.classroomsDiv.classList.add('classroom-container');    this.selectSubjectInput.innerHTML = subjects.map((option, index) => (      `<option key=${index} value=${option}>${option.toUpperCase()}</option>`    ));    this.addclassBtn.textContent = 'New Class';    this.addclassBtn.addEventListener('click', () => this.addClassroom());    this.classroomSectionDiv.append(      this.classroomsDiv, this.selectSubjectInput,      this.addclassBtn,      );    appDiv.appendChild(this.classroomSectionDiv);  }  updateView() {    const { classroomController, classroomsDiv } = this;    const allClassrooms = classroomController.classrooms.map(      c => {        const removeBtn = document.createElement('button');        const classDiv = document.createElement('div');        classDiv.classList.add('classroom');        if (classroomController.selectedClass === c) {          classDiv.classList.add('selected');        }        classDiv.addEventListener('click', () => this.selectClassroom(classDiv.getAttribute('data-classroom-id')));        classDiv.setAttribute('data-classroom-id', c.id);        removeBtn.addEventListener('click', () => this.removeClassroom(removeBtn.getAttribute('data-classroom-id')));        removeBtn.setAttribute('data-classroom-id', c.id);        removeBtn.classList.add('remove-btn');        removeBtn.textContent= 'remove';        const allPersons = c.persons.map(p => (          `<div class="person-inline">            <span class="fname">${p.firstName}</span>            <span class="lname">${p.lastName}</span>            <span class="${p.occupation}">${p.occupation}</span>          </div>`        ));        classDiv.innerHTML = `<div class="m-b">            <span class="id">${c.id}</span>            <span class="subject">${c.subject}</span></div>            <div class="all-persons">${allPersons.join('')}</div>`;        classDiv.appendChild(removeBtn);        return classDiv;      }    );    classroomsDiv.innerHTML='';    allClassrooms.map(div => classroomsDiv.append(div));  }    selectClassroom(classroomID) {    const { classroomController } = this;    classroomController.selectClassroom(classroomID);     this.updateView();  }  addClassroom() {    const {      classroomController,      selectSubjectInput,    } = this;    const subjectChosen = selectSubjectInput.value;    classroomController.addClassroom(subjectChosen);    this.updateView();  }  removeClassroom(classroomID) {    const { classroomController } = this;    classroomController.removeClassroom(classroomID);    this.updateView();  }  addPerson(person, classroomID) {    const { classroomController } = this;    classroomController.addPerson(person, classroomID);    this.updateView();  }}

Класс ClassroomView содержит переменную, которая связана с ClassroomController, который создается при конструкции. Это позволяет уровню представления общаться с контроллером.

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

Все функции в представлении просто захватывают значения из UI элементов DOM и передают их как переменные функциям контроллера. Функции selectClassroom(), addClassroom() и removeClassroom() добавляются к элементам DOM через функцию updateView() как события через функцию addEventListener().

Доступ ко всем контроллерам и представлениям с помощью одного представления

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

import classroomView from './classroom';import personView from './person';class AppView {  constructor(appDiv) {    this.classroomView = new classroomView(appDiv);    this.personView = new personView(appDiv);    this.addPersonToClassBtn = document.createElement('button');    this.addPersonToClassBtn.textContent = 'Add selected Person to Selected Class';    this.addPersonToClassBtn.addEventListener('click', () => this.addPersonToClass());    appDiv.appendChild(this.addPersonToClassBtn);  }  addPersonToClass() {    const { classroomView, personView } = this;    const { classroomController } = classroomView;    const { personController } = personView;    const selectedClassroom = classroomController.selectedClass;    const selectedPerson = personController.selectedPerson;    classroomView.addPerson(selectedPerson, selectedClassroom);    personView.updateView();  }}

Класс AppView будет иметь собственные переменные, которые будут связываться как с ClassroomView, так и с PersonView. Поскольку он имеет доступ к этим двум представлениям, он также имеет доступ и к их контроллерам.

Кнопка выше создается AppView. Оно получает значения selectedClassroom и selectedPerson из соответствующих контроллеров и при взаимодействии запускает функцию addPerson() в ClassroomView.

Чтобы полностью посмотреть приложение Classroom, переходите в CodeSandBox по этой ссылке.

Некоторые преимущества использования структуры MVC

Источники: Brainvire, c-sharpcorner, StackOverflow, Wikipedia

1. Разделение обязанностей

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

2. Одновременная разработка

Поскольку модель MVC четко разделяет проект на три (3) уровня, становится намного проще поделить и распределить задачи между несколькими разработчиками.

3. Простота модификации

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

4. Разработка через тестирование (TDD)

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


Узнать подробнее о курсе "Архитектура и шаблоны проектирования".

Зарегистрироваться на открытый вебинар на тему "Интерпретатор".

Прямо сейчас в OTUS действуют максимальные новогодние скидки на все курсы. Ознакомиться с полным списком курсов вы можете по ссылке ниже. Также у всех желающих есть уникальная возможность отправить адресатуподарочный сертификат на обучение в OTUS.

Кстати, о "красивой упаковке" онлайн-сертификатов мырассказываем в этой статье.

ЗАБРАТЬ СКИДКУ

Подробнее..

Перевод Что из себя представляет класс Startup и Program.cs в ASP.NET Core

15.02.2021 16:13:26 | Автор: admin

В преддверии старта курса C# ASP.NET Core разработчик подготовили традиционный перевод полезного материала.

Также приглашаем на открытый вебинар по теме
Отличия структурных шаблонов проектирования на примерах. На вебинаре участники вместе с экспертом рассмотрят три структурных шаблона проектирования: Заместитель, Адаптер и Декоратор; а также напишут несколько простых программ и проведут их рефакторинг.


Введение

Program.cs это место, с которого начинается приложение. Файл Program.cs в ASP.NET Core работает так же, как файл Program.cs в традиционном консольном приложении .NET Framework. Файл Program.cs является точкой входа в приложение и отвечает за регистрацию и заполнение Startup.cs, IISIntegration и создания хоста с помощью инстанса IWebHostBuilder, метода Main.

Global.asax больше не входит в состав приложения ASP.NET Core. В ASP.NET Core заменой файла Global.asax является файл Startup.cs.

Файл Startup.cs это также точка входа, и он будет вызываться после выполнения файла Program.cs на уровне приложения. Он обрабатывает конвейер запросов. Класс Startup запускается в момент запуска приложения.

Описание

Что такое Program.cs?

Program.cs это место, с которого начинается приложение. Файл класса Program.cs является точкой входа в наше приложение и создает инстанс IWebHost, на котором размещается веб-приложение.

public class Program {      public static void Main(string[] args) {          BuildWebHost(args).Run();      }      public static IWebHost BuildWebHost(string[] args) => WebHost.CreateDefaultBuilder(args).UseStartup < startup > ().Build();  }  

WebHost используется для создания инстансов IWebHost, IWebHostBuilder и IWebHostBuilder, которые имеют предварительно настроенные параметры. Метод CreateDefaultBuilder() создает новый инстанс WebHostBuilder.

Метод UseStartup() определяет класс Startup, который будет использоваться веб-хостом. Мы также можем указать наш собственный пользовательский класс вместо Startup.

Метод Build() возвращает экземпляр IWebHost, а Run() запускает веб-приложение до его полной остановки.

Program.cs в ASP.NET Core упрощает настройку веб-хоста.

public static IWebHostBuilder CreateDefaultBuilder(string[] args) {      var builder = new WebHostBuilder().UseKestrel().UseContentRoot(Directory.GetCurrentDirectory()).ConfigureAppConfiguration((hostingContext, config) => {          /* setup config */ }).ConfigureLogging((hostingContext, logging) => {          /* setup logging */ }).UseIISIntegration()      return builder;  }

Метод UseKestrel() является расширением, которое определяет Kestrel как внутренний веб-сервер. Kestrel это кроссплатформенный веб-сервер для ASP.NET Core с открытым исходным кодом. Приложение работает с модулем Asp.Net Core, и необходимо включить интеграцию IIS (UseIISIntegration()), которая настраивает базовый адрес и порт приложения.

Он также настраивает UseIISIntegration(), UseContentRoot(), UseEnvironment(Development), UseStartup() и другие доступные конфигурации, например Appsetting.json и Environment Variable. UseContentRoot используется для обозначения текущего пути к каталогу.

Мы также можем зарегистрировать логирование и установить минимальный loglevel, как показано ниже. Это также переопределитloglevel, настроенный в файле appsetting.json.

.ConfigureLogging(logging => { logging.SetMinimumLevel(LogLevel.Warning); }) 

Таким же образом мы можем контролировать размер тела нашего запроса и ответа, включив эту опцию файле program.cs, как показано ниже.

.ConfigureKestrel((context, options) => { options.Limits.MaxRequestBodySize = 20000000; });  

ASP.net Core является кроссплатформенным и имеет открытый исходный код, а также его можно размещать на любом сервере (а не только на IIS, внешнем веб-сервере), таком как IIS, Apache, Nginx и т. д.

Что такое файл Startup?

Обязателен ли файл startup.cs или нет? Да, startup.cs является обязательным, его можно специализировать любым модификатором доступа, например, public, private, internal. В одном приложении допускается использование нескольких классов Startup. ASP.NET Core выберет соответствующий класс в зависимости от среды.

Если существует класс Startup{EnvironmentName}, этот класс будет вызываться для этого EnvironmentName или будет выполнен файл Startup для конкретной среды в целом (Environment Specific), чтобы избежать частых изменений кода/настроек/конфигурации в зависимости от среды.

ASP.NET Core поддерживает несколько переменных среды, таких как Development, Production и Staging. Он считывает переменную среды ASPNETCORE_ENVIRONMENT при запуске приложения и сохраняет значение в интерфейсе среды хоста (into Hosting Environment interface).

Обязательно ли этот класс должен называться startup.cs? Нет, имя класса не обязательно должно быть Startup.

Мы можем определить два метода в Startup файле, например ConfigureServices и Configure, вместе с конструктором.

Пример Startup файла

public class Startup {      // Use this method to add services to the container.      public void ConfigureServices(IServiceCollection services) {          ...      }      // Use this method to configure the HTTP request pipeline.      public void Configure(IApplicationBuilder app) {          ...      }  }  

Метод ConfigureServices

Это опциональный метод в классе Startup, который используется для настройки сервисов для приложения. Когда в приложение поступает какой-либо запрос, сначала вызывается метод ConfigureService.

Метод ConfigureServices включает параметр IServiceCollection для регистрации сервисов. Этот метод должен быть объявлен с модификатором доступа public, чтобы среда могла читать контент из метаданных.

public void ConfigureServices(IServiceCollection services)  {     services.AddMvc();  }  

Метод Configure

Метод Configure используется для указания того, как приложение будет отвечать на каждый HTTP-запрос. Этот метод в основном используется для регистрации промежуточного программного обеспечения (middleware) в HTTP-конвейере. Этот метод принимает параметр IApplicationBuilder вместе с некоторыми другими сервисами, такими как IHostingEnvironment и ILoggerFactory. Как только мы добавим какой-либо сервис в метод ConfigureService, он будет доступен для использования в методе Configure.

public void Configure(IApplicationBuilder app)  {     app.UseMvc();  }

В приведенном выше примере показано, как включить функцию MVC в нашем фреймворке. Нам нужно зарегистрировать UseMvc() в Configure и сервис AddMvc() в ConfigureServices. UseMvc является промежуточным программным обеспечением. Промежуточное программное обеспечение (Middleware) это новая концепция, представленная в Asp.net Core. Для вас доступно множество встроенных промежуточных программ, некоторые из которых указаны ниже.

app.UseHttpsRedirection();  app.UseStaticFiles();  app.UseRouting();  app.UseAuthorization();  UseCookiePolicy();  UseSession(); 

Промежуточное программное обеспечение можно настроить в http-конвейере с помощью команд Use, Run и Map.

Run

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

Use

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

Map

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

app.Map("/MyDelegate", MyDelegate);

Чтобы получить более подробную информацию о промежуточном программном обеспечении, переходите сюда

ASP.net Core имеет встроенную поддержку внедрения зависимостей (Dependency Injection). Мы можем настроить сервисы для контейнера внедрения зависимостей, используя этот метод. Следующие способы конфигурационные методы в Startup классе.

AddTransient

Transient (временные) объекты всегда разные; каждому контроллеру и сервису предоставляется новый инстанс.

Scoped

Scoped используются одни и те же объекты в пределах одного запроса, но разные в разных запросах.

Singleton

Singleton объекты одни и те же для каждого объекта и каждого запроса.

Можем ли мы удалить startup.cs и объединить все в один класс с Program.cs?

Ответ: Да, мы можем объединить все классы запуска в один файл.

Заключение

Вы получили базовое понимание того, почему файлы program.cs и startup.cs важны для нашего приложения Asp.net Core и как их можно настроить. Мы также немного познакомились с переменной среды (Environment Variable), внедрение зависимостей, промежуточным программным обеспечением и как его настроить. Кроме того, мы увидели, как можно настроить UseIIsintegration() и UseKestrel().


Узнать подробнее о курсе C# ASP.NET Core разработчик.

Смотреть открытый вебинар по теме Отличия структурных шаблонов проектирования на примерах.

Подробнее..

Шаблон микросервиса зачем нужен и как его внедрить в разработку

19.02.2021 12:19:30 | Автор: admin

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

В том или ином виде шаблоны используют многие разработчики. Чаще всего, чтобы сэкономить время, они копируют код из старых проектов. С нашей точки зрения, у такой практики есть сразу несколько недостатков:

1. Можно выбрать неудачный микросервис для копирования - с недостающими элементами, некорректными настройками и т.д.

2. Если у разработчиков нет эталона, повышается риск дополнительных ошибок. Значит, кому-то придётся потратить лишнее время, чтобы их исправить.

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

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

Что даёт шаблон микросервиса

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

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

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

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

  • Чем меньше времени уходит на лишние действия, тем меньше time-to-market. Релизы поставляются чаще, продукт развивается быстрее.

  • Шаблон помогает выполнять стандарты Production Ready. Это включает в себя не только архитектурные элементы, которые должны быть в каждом продукте, но и другие важные вещи вроде средств работы с метриками и Feature Flags, базовых тестов.

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

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

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

Что фиксируется в шаблоне

  1. Структура папок и базовые классы необходимые для любого микросервиса

  2. Настройки Rest API, баз данных, авторизации, брокеров сообщений

  3. Правильные версии базовых библиотек

  4. Наши внутренние наработки и настройки

    • Библиотеки собственной разработки

    • Пайплайны сборки и поставки (CI/CD)

    • То, что называется Observability, а именно логирование, трассировка, health check monitoring

  5. Помимо самого кода, мы стандартизуем всю экосистему для нашего будущего микросервиса: настраиваем политики работы Git репозитория, пайплайны Jenkins/Gitlab.

Как выглядит наш шаблон микросервиса для .Net

Наш базовый микросервис является расширением Microsoft Visual Studio и объединяет в себе пять компонентов:

  • Web API Project - бэк-сервис, который отвечает на HTTP-запросы.

  • Data base project база данных микросервиса.

  • HTTP Service client project позволяет вызывать методы в сервисе бэка

  • MassTransit consumers project обеспечивает получение сообщений из RabbitMQ.

  • Job Project применяется для выполнения каких-либо действий по расписанию.

Модули можно добавить в проект сразу все разом или по отдельности. Выбор сделан на основе простого UI, где достаточно галочками прощёлкать нужные компоненты:

Готовый шаблон помещается в маркетплейс Visual Studio или другой репозиторий, который позволит вам автоматически обновлять его содержимое.

Итоги

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

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

Подробнее..

Перевод Реализуем глобальную обработку исключений в ASP.NET Core приложении

20.02.2021 18:04:03 | Автор: admin

В преддверии старта курса C# ASP.NET Core разработчик подготовили традиционный перевод полезного материала.

Также рекомендуем посмотреть вебинар на тему
Отличия структурных шаблонов проектирования на примерах. На этом открытом уроке участники вместе с преподавателем-экспертом познакомятся с тремя структурными шаблонами проектирования: Заместитель, Адаптер и Декоратор.


Введение

Сегодня в этой статье мы обсудим концепцию обработки исключений в приложениях ASP.NET Core. Обработка исключений (exception handling) одна из наиболее важных импортируемых функций или частей любого типа приложений, которой всегда следует уделять внимание и правильно реализовывать. Исключения это в основном средства ориентированные на обработку рантайм ошибок, которые возникают во время выполнения приложения. Если этот тип ошибок не обрабатывать должным образом, то приложение будет остановлено в результате их появления.

В ASP.NET Core концепция обработки исключений подверглась некоторым изменениям, и теперь она, если можно так сказать, находится в гораздо лучшей форме для внедрения обработки исключений. Для любых API-проектов реализация обработки исключений для каждого действия будет отнимать довольно много времени и дополнительных усилий. Но мы можем реализовать глобальный обработчик исключений (Global Exception handler), который будет перехватывать все типы необработанных исключений. Преимущество реализации глобального обработчика исключений состоит в том, что нам нужно определить его всего лишь в одном месте. Через этот обработчик будет обрабатываться любое исключение, возникающее в нашем приложении, даже если мы объявляем новые методы или контроллеры. Итак, в этой статье мы обсудим, как реализовать глобальную обработку исключений в ASP.NET Core Web API.

Создание проекта ASP.NET Core Web API в Visual Studio 2019

Итак, прежде чем переходить к обсуждению глобального обработчика исключений, сначала нам нужно создать проект ASP.NET Web API. Для этого выполните шаги, указанные ниже.

  • Откройте Microsoft Visual Studio и нажмите Create a New Project (Создать новый проект).

  • В диалоговом окне Create New Project выберите ASP.NET Core Web Application for C# (Веб-приложение ASP.NET Core на C#) и нажмите кнопку Next (Далее).

  • В окне Configure your new project (Настроить новый проект) укажите имя проекта и нажмите кнопку Create (Создать).

  • В диалоговом окне Create a New ASP.NET Core Web Application (Создание нового веб-приложения ASP.NET Core) выберите API и нажмите кнопку Create.

  • Убедитесь, что флажки Enable Docker Support (Включить поддержку Docker) и Configure for HTTPS (Настроить под HTTPS) сняты. Мы не будем использовать эти функции.

  • Убедитесь, что выбрано No Authentication (Без аутентификации), поскольку мы также не будем использовать аутентификацию.

  • Нажмите ОК.

Используем UseExceptionHandler middleware в ASP.NET Core.

Чтобы реализовать глобальный обработчик исключений, мы можем воспользоваться преимуществами встроенного Middleware ASP.NET Core. Middleware представляет из себя программный компонент, внедренный в конвейер обработки запросов, который каким-либо образом обрабатывает запросы и ответы. Мы можем использовать встроенное middleware ASP.NET Core UseExceptionHandler в качестве глобального обработчика исключений. Конвейер обработки запросов ASP.NET Core включает в себя цепочку middleware-компонентов. Эти компоненты, в свою очередь, содержат серию делегатов запросов, которые вызываются один за другим. В то время как входящие запросы проходят через каждый из middleware-компонентов в конвейере, каждый из этих компонентов может либо обработать запрос, либо передать запрос следующему компоненту в конвейере.

С помощью этого middleware мы можем получить всю детализированную информацию об объекте исключения, такую как стектрейс, вложенное исключение, сообщение и т. д., а также вернуть эту информацию через API в качестве вывода. Нам нужно поместить middleware обработки исключений в configure() файла startup.cs. Если мы используем какое-либо приложение на основе MVC, мы можем использовать middleware обработки исключений, как это показано ниже. Этот фрагмент кода демонстрирует, как мы можем настроить middleware UseExceptionHandler для перенаправления пользователя на страницу с ошибкой при возникновении любого типа исключения.

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)  {      app.UseExceptionHandler("/Home/Error");      app.UseMvc();  } 

Теперь нам нужно проверить сообщение об исключении. Для этого откройте файл WeatherForecastController.cs и добавьте следующий экшн-метод, чтобы пробросить исключение:

[Route("GetExceptionInfo")]  [HttpGet]  public IEnumerable<string> GetExceptionInfo()  {       string[] arrRetValues = null;       if (arrRetValues.Length > 0)       { }       return arrRetValues;  } 

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

app.UseExceptionHandler(                  options =>                  {                      options.Run(                          async context =>                          {                              context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;                              context.Response.ContentType = "text/html";                              var exceptionObject = context.Features.Get<IExceptionHandlerFeature>();                              if (null != exceptionObject)                              {                                  var errorMessage = $"<b>Exception Error: {exceptionObject.Error.Message} </b> {exceptionObject.Error.StackTrace}";                                  await context.Response.WriteAsync(errorMessage).ConfigureAwait(false);                              }                          });                  }              );  

Для проверки вывода просто запустите эндпоинт API в любом браузере:

Определение пользовательского Middleware для обработки исключений в API ASP.NET Core

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

using Microsoft.AspNetCore.Http;    using Newtonsoft.Json;    using System;    using System.Collections.Generic;    using System.Linq;    using System.Net;    using System.Threading.Tasks;        namespace API.DemoSample.Exceptions    {        public class ExceptionHandlerMiddleware        {            private readonly RequestDelegate _next;                public ExceptionHandlerMiddleware(RequestDelegate next)            {                _next = next;            }                public async Task Invoke(HttpContext context)            {                try                {                    await _next.Invoke(context);                }                catch (Exception ex)                {                                    }            }        }    } 

В приведенном выше классе делегат запроса передается любому middleware. Middleware либо обрабатывает его, либо передает его следующему middleware в цепочке. Если запрос не успешен, будет выброшено исключение, а затем будет выполнен метод HandleExceptionMessageAsync в блоке catch. Итак, давайте обновим код метода Invoke, как показано ниже:

public async Task Invoke(HttpContext context)  {      try      {          await _next.Invoke(context);      }      catch (Exception ex)      {          await HandleExceptionMessageAsync(context, ex).ConfigureAwait(false);      }  }  

Теперь нам нужно реализовать метод HandleExceptionMessageAsync, как показано ниже:

private static Task HandleExceptionMessageAsync(HttpContext context, Exception exception)  {      context.Response.ContentType = "application/json";      int statusCode = (int)HttpStatusCode.InternalServerError;      var result = JsonConvert.SerializeObject(new      {          StatusCode = statusCode,          ErrorMessage = exception.Message      });      context.Response.ContentType = "application/json";      context.Response.StatusCode = statusCode;      return context.Response.WriteAsync(result);  } 

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

using Microsoft.AspNetCore.Builder;  using System;  using System.Collections.Generic;  using System.Linq;  using System.Threading.Tasks;    namespace API.DemoSample.Exceptions  {      public static class ExceptionHandlerMiddlewareExtensions      {          public static void UseExceptionHandlerMiddleware(this IApplicationBuilder app)          {              app.UseMiddleware<ExceptionHandlerMiddleware>();          }      }  }  

На последнем этапе, нам нужно включить наше пользовательское middleware в методе Configure класса startup, как показано ниже:

app.UseExceptionHandlerMiddleware();

Заключение

Обработка исключений это по сути сквозная функциональность для любого типа приложений. В этой статье мы обсудили процесс реализации концепции глобальной обработки исключений. Мы можем воспользоваться преимуществами глобальной обработки исключений в любом приложении ASP.NET Core, чтобы гарантировать, что каждое исключение будет перехвачено и вернет правильные сведения, связанные с этим исключением. С глобальной обработкой исключений нам достаточно в одном месте написать код, связанный с обработкой исключений, для всего нашего приложения. Любые предложения, отзывы или запросы, связанные с этой статьей, приветствуются.


Узнать подробнее о курсе C# ASP.NET Core разработчик.

Посмотреть вебинар на тему Отличия структурных шаблонов проектирования на примерах.

Подробнее..

Strategy Design Pattern

13.04.2021 22:15:19 | Автор: admin

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

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

В чем суть?

Design patter Strategy или шаблон проектирования Стратегия относится к поведенческим шаблонам проектирования. Его задача - выделить схожие алгоритмы, решающие конкретную задачу. Реализация алгоритмов выносится в отдельные классы и предоставляется возможность выбирать алгоритмы во время выполнения программы.

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

В чем проблема?

Рассмотрим задачи, при решении которых можно применять такой подход.

Представьте, что перед вами стоит задача написать веб-портал по поиску недвижимости. MVP (Minimum ViableProduct) или минимально работающий продукт был спроектирован и приоритизирован вашей командой Product Managerов и на портале должен появиться функционал для покупателей квартир. То есть целевые пользователи вашего продукта в первую очередь - это те, кто ищет себе новое жилье для покупки. Одной из самых востребованных функций должна быть возможность:

  • Выбрать область на карте, где покупатель желает приобрести жилье

  • И указать ценовой диапазон цен на квартиры для фильтрации.

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

Но тут приходят к вам Product Manager'ы и говорят, что нужно добавить возможность искать и отображать недвижимость, которая сдается в аренду. У нас появляется еще один тип пользователя - арендаторы. Для арендаторов не так важно показывать фильтры по цене, им важно состояние квартиры, поэтому нужно отображать фотографии арендуемых квартир.

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

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

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

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

  • Основной алгоритм поиска квартир был реализован в одном супер-классе

  • Алгоритм выбора и отображения элементов интерфейса был реализован в одном супер-классе

  • Изменения в этих классах, сделанные разными программистами, приводили к конфликтам и необходимости регрессивного тестирования

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

  • Больше времени стало уходить на разработку, тестирование, появилось множество багов.

Супер-класс с единым методом реализации алгоритма.Супер-класс с единым методом реализации алгоритма.

Какое решение?

В данном примере мы имеем несколько алгоритмов для одной функции:

  • Поиск квартир с продажей

  • Поиск квартир в аренду

  • Отображение или нет различных наборов фильтров

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

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

Чтобы работа нашего класса была одинаковой для разного поведения, у объектов-стратегии должен быть общий интерфейс. Используя такой интерфейс вы делаете независимым наш класс-контекста от классов-стратегий.

Диаграмма классов шаблона StrategyДиаграмма классов шаблона Strategy

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

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

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

Задача контроллера определить класс-стратегию и запросить у класса-контекста данные для отображения, передав ему известный набор фильтров. Класс-контекст в этой схеме - это класс, которые реализует метод поиска квартир по заданным фильтрам. На диаграмме классов выше мы видим, что класс контекста определяет метод getData, и принимает аргументы filters. У него должен быть конструктор, принимающий активный в данный момент объект-стратегии и сеттерsetStrategy, устанавливающий активную стратегию. Такой метод пригодится для случая, когда пользователь меняет тип искомого объекта, например, он ищет недвижимость на продажу и хочет снять квартиру.

Пример реализации

Ниже рассмотрим пример, как решается описанная задача на языке GOlang. Первое что сделаем - определим интерфейс с методом doSearch:

Strategy.goStrategy.go

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

Для реализации конкретных алгоритмов создаем два файла. В каждом файле определяется свой определяемый тип с базовым типом struct, реализующие интерфейс Strategy. Соответственно, в методы, определяемые интерфейсом для каждого алгоритма, будут передаваться пользовательские фильтры. Реализации выглядит следующим образом:

FirstAlgorithm.goFirstAlgorithm.goSecondAlgorithm.goSecondAlgorithm.go

Посмотрим на нашу диаграмму классов. Нам осталось реализовать класс-контекста и клиентский код вызова конкретных алгоритмов в нужным момент. Как это сделать? Для создания слоя класса-контекста реализуем исходник, реализующий:

  • определяемый тип в базовым типом struct

  • функцию initStrategy, инициализирующий стратегию по-умолчанию и пользовательские фильтры

  • метод типа struct setStrategy, устанавливающий активную стратегию

  • и функция getData, вызывающий конкретную стратегию и возвращаемый данные для показа пользователю.

Context.goContext.go

Последний шаг - посмотрим на клиентский код. Сначала инициализируется стратегия по-умолчанию, получаем данные из этой стратегии. Затем выставляем новую активную стратегию и повторно вызываем функцию получения данных getData. Как видим, каждый раз вызывается одна и та же функция файла-контекста (или класса-контекста в диаграмме классов) для получения данных рендеринга, отображаемых в дальнейшем пользователю. Для простоты я вывожу отладочную информацию в консоль, чтобы убедиться, что работают разные алгоритмы при вызовах. Вот код:

Client.goClient.go

Вот вывод такого подхода:

First implements strategy map[role:1]

Second implements strategy map[role:2]

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

Объектно-ориентированный подход можно посмотреть. например, в этом курсе. Там показан пример на PHP.

Когда применять?

Напоследок поговорим когда применяется шаблон Strategy?

  1. У вас есть множество похожих реализаций отличающихся незначительным поведением. Можно вынести отличающее поведение в классы-стратегии, а повторяющий код свести к единому классу-контекста.

  2. Ваш алгоритм реализован в супер-классе с множественными условными операторами. Выделите блоки условных операторов в отдельные классы-стратегии, а управление вызовов нужных доверьте классу-контекста.

  3. Конкретные стратегии позволяют инкапсулировать алгоритмы в своих конкретных классах. Используйте этот подход для снижения зависимостей от других классов.

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

Подведем итог

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

Рад был с вами пообщаться, Alex Versus. Успехов!

Подробнее..

Разделяй и властвуй Использование FSM в Unity

23.04.2021 22:09:05 | Автор: admin

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

Минимальный аниматор главного героя в платформереМинимальный аниматор главного героя в платформере

Аниматоры в Unity построены как раз на конечных автоматах. Каждая анимация группы объектов представлена в виде состояния. Условия и порядок переходов между ними определяется в аниматоре, который является конечным автоматом. Также, неоднократно поднималась тема использования конечных автоматов для описания логики работы объектов со сложным поведением. AI ботов, управление главным героем, вот это все.

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

  • Каждое состояние описывает свою логику максимально просто. Нет никаких условных блоков, каждое состояние представляет собой последовательность действий на входе и выходе и список сигналов, которые автомат обрабатывает в текущем состоянии. Таким образом, мы отображаем джойстик на входе в состояние Play и скрываем на выходе. Но даже если каким-то чудом игрок увидел джойстик на экране паузы, он не сможет двигать персонажа. И не потому, что активен еще один флаг isPaused, а просто потому, что в состоянии паузы не предусмотрено принимать сигналы с джойстика.

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

  • В дополнение к предыдущему пункту, следует отметить, что это также очень удобно в контексте дебага. Посмотрев логи, мы сразу видим, что бот сломался потому, что был инициализирован не после старта уровня, а во время генерации сцены, когда еще не был готов контекст для работы его AI.

  • Многие баги становятся просто невозможны, потому что мы строго определяем условия переходов. Мы точно не попадем в состояние Play, пока состояние WaitMatch не получит сигнал "match_ready", а если мы захотим вернуться в лобби, мы сначала отправим серверу команду об этом, и только после сигнала "room_left" выполним переход.

  • Сама идея логики на автоматах максимально прозрачна. Видя ТЗ мы сразу понимаем, каким будет список состояний, логика, реализованная в каждом из них и граф переходов. Причем, как было отмечено выше, "внешняя" часть графа в большинстве игр будет оставаться неизменной.

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

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

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

FSM

AState

- public FSM(AState initState)

- public void Signal(string name, object data = null)

- private void ChangeState(AState newState)

- void Enter()

- void Exit()

- AState Signal()

Итак, мы имеем 2 сущности:

Конечный автомат. Интерфейс у него невелик. Начиная работу в некотором состоянии, он может принимать сигналы извне, передавая их в текущее состояние. И если оно в ответ на сигнал вернуло новое состояние, автомат выполняет переход в него. В момент перехода мы выполняем Exit для последнего состояния и Enter для следующего. Таким образом, у нас получается следующая конструкция:

 public class FSM {   private AState currentState;   public FSM(AState initState) => ChangeState(initState);      private void ChangeState(AState newState)   {     if (newState == null) return;     currentState?.Exit();     currentState = newState;     currentState.Enter();   }   public void Signal(string name, object arg = null)   {     var result = currentState.Signal(name, arg);     ChangeState(result);   } }

Абстрактное состояние. Из соображений удобства и микрооптимизации его методы имеют нехитрую реализацию, которая наследуется потомками, в которых оные не определены.

public class AState{  public virtual void Enter() => null;  public virtual void Exit() => null;  public virtual AState Signal(string name, object arg) => null;}

А в самих состояниях мы просто описываем логику

public class SLoad : AState{    public override void Enter()    {        Game.Data.Set("loader_visible",true);        var load = SceneManager.LoadSceneAsync("SceneGameplay");        load.completed+=a=>Game.Fsm.Signal("scene_loaded");    }    public override void Exit()    {        Game.Data.Set("loader_visible",false);    }        public override AState Signal(string name, object arg)    {        if (name == "scene_loaded")            return new SLobby();        return null;    }    }

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

public class SMessage : AState{    private string msgText;    private AState next;    public SMessage(string messageText, AState nextState)    {        msgText = messageText;        btnText = buttonText;        next = nextState;    }        public override void Enter()    {        Game.Data.Set("message_text", msgText);        Game.Data.Set("window_message_visible",true);    }    public override void Exit()    {        Game.Data.Set("window_message_visible",false);    }        public override AState Signal(string name, object arg)    {        if (name == "message_btn_ok")             return next;        return null;    }}

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

...case "iap_ok":return new SMessage("Item purchased! Going back to store.", new SStore());...

В этой статье я не буду задерживаться на устройстве структуры Game.Data, используемой в состояниях, достаточно просто отметить, что это словарь, в элементах которого реализован шаблон "Наблюдатель". Объекты сцены, например, элементы UI, могут подписываться на изменения в нем и отображать гарантированно актуальные данные. Она играет ключевую роль в связи между визуализацией и логикой в моих примерах, но не является необходимым условием для работы с конечными автоматами. Сигналы в автомат же можно отправлять откуда угодно, но одним из самых универсальных примеров будет следующий скрипт.

public class ButtonFSM : MonoBehaviour, IPointerClickHandler{    public string key;        public override void OnPointerClick(PointerEventData eventData)    {        Game.Fsm.Signal(key);    }}

Иными словами, мы при клике по кнопке(на самом деле, любому CanvasRenderer) передаем соответствующий сигнал в автомат. При переходе между состояниями мы можем любым удобным нам способом включать и выключать разные Canvas, менять маски, используемые в Physics.Raycast и даже иногда менять Time.timeScale! Как бы ужасно и бескультурно это ни казалось на первый взгляд, пока сделанное в Enter отменяется в Exit, оно гарантированно не может доставить каких-либо неудобств, так что вперед! Главное - не переусердствуйте.

Подробнее..

Prototype Design Pattern в Golang

24.05.2021 18:21:26 | Автор: admin

Привет друзья! С вами Алекс и я продолжаю серию статей, посвящённых применению шаблонов проектирования в языке Golang.

Интересно получать обратную связь от вас, понимать на сколько применима данная область знаний в мире языка Golang. Ранее уже рассмотрели шаблоны: Simple Factory, Singleton и Strategy. Сегодня хочу рассмотреть еще один шаблон проектирования - Prototype.

Для чего нужен?

Это порождающий шаблон проектирования, который позволяет копировать объекты, не вдаваясь в подробности их реализации.

Какую проблему решает?

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

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

Какое решение?

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

Реализация этого метода в разных классах очень схожа. Метод создаёт новый объект текущего класса и копирует в него значения всех полей собственного объекта. Так получится скопировать даже приватные поля, так как большинство языков программирования разрешает доступ к приватным полям любого объекта текущего класса. Объект, который копируют, называется прототипом, отсюда и название шаблона. Когда объекты программы содержат сотни полей и тысячи возможных конфигураций, прототипы могут служить своеобразной альтернативой созданию подклассов. Шаблон прототип должен копировать объекты любой сложности без привязки к их конкретным классам.

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

Диаграмма классов

Prototype Class DiagramPrototype Class Diagram

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

Как реализовать?

Подробно пошаговую реализацию данного шаблона, а также других шаблонов проектирования на языке PHP можно посмотреть тут. Наша задача реализовать шаблон Prototype на языке Golang.

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

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

Каждую рубрика, как конечный элемент рубрикатора, может быть представлен интерфейсом prototype, который объявляет функцию clone. За основу конкретных прототипов рубрики и раздела мы берем тип struct, которые реализуют функции show и clone интерфейса prototype.

Итак, реализуем интерфейс прототипа. Далее мы реализуем конкретный прототип directory, который реализует интерфейс prototype представляет раздел рубрикатора. И конкретный прототип для рубрики. Обе структуру реализуют две функции show, которая отвечает за отображение конкретного контента ноды и clone для копирования текущего объекта. Функция clone в качестве единственного параметра принимает аргумент, ссылающийся на тип указателя на структуру конкретного прототипа - это либо рубрика, либо директория. И возвращает указатель на поле структуры, добавляя к наименованию поля _clone.

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

Open directory 2  Directory 2    Directory 1        category 1    category 2    category 3Clone and open directory 2  Directory 2_clone    Directory 1_clone        category 1_clone    category 2_clone    category 3_clone

Когда применять?

  1. У вас много объектов с множеством свойств и вам нужно создавать их клоны быстро и эффективно. Шаблон предлагает использовать набор прототипов, вместо создания подклассов для описания популярных конфигураций объектов. Вместо порождения объектов из подклассов, вы копируете существующие объекты-прототипы, в которых уже настроено состояние. Тем самым избегая роста количества классов в программе и уменьшая её сложность.

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

Итог

Друзья, шаблон Prototype предлагает:

  • Удобную концепцию для создания копий объектов.

  • Помогает избежать дополнительных усилий по созданию объекта стандартным путём, когда это непозволительно дорого для приложения.

  • В объектных языках позволяет избежать наследования создателя объекта в клиентском приложении, как это делает паттерн abstract factory, например.

Кстати, друзья, вот тут можно посмотреть результаты опроса читателей хабра. 63% опрошенных считают, что применение шаблонов проектирования в Golang - это зло. Связано, скорее всего, с тем, что язык Golang процедурный и ему чужды подходы объектно-ориентированных языков. Но рассматривать реализации и применение шаблонов стоит, так как это позволяет больше их понимать и периодически применять для решения тех или иных задач. Каждый подход требует, конечно, дискуссии и разумного применения.

Друзья, рад был поделиться темой, Алекс. На английском статью можно найти тут.
Удачи!

Подробнее..

Подпишись, чтобы не пропустить События

16.06.2021 20:20:54 | Автор: admin

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

В примерах в статье про автоматы я использовал следующую конструкцию:

Game.Event.Invoke("joystick_updated", input);

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

public static class Game{    public static FSM Fsm = new FSM();    public static EventManager Event = new EventManager();       public static ObservableData Data = new ObservableData();...

В этих примерах можно увидеть некоторые вольности в деталях реализации. При масштабировании проекта, например, придется отказаться от статического контекста и на основе класса Game реализовать компоненты, назовем их претенциозно MonoBehaviourPro с подобной структурой для сложных подсистем, и передавать ее в качестве контекста автомату и компонентам этих подсистем. Я намеренно сглаживаю эти углы для большей наглядности примера. Сегодня мы рассмотрим класс с многострадальным названием EventManager, так как он является зависимостью ObservableData и без него мы не сможем двинуться дальше. По ссылке можно увидеть полную реализацию класса EventManager, принцип его работы предельно прост. Мы храним список делегатов c произвольной сигнатурой, подписанных на события со строковым ключом.

Важно, что мы работаем с Generic-структурой, поэтому следует помнить о Type safety. Тип аргумента при отправке события должен соответствовать сигнатурам функций, подписанных на него. Также, можно заметить, что EventManager отдельно хранит binds и binds_global и имеет отдельный интерфейс для работы с ними. Это реализация, специфичная для Unity. Дело в том, что там существует система сцен, позволяющая подгружать или выгружать сцены и объекты. И разница между этими двумя словарями в том, что первый очищается при выгрузке сцены. В идеальном мире мы всегда подписываем объект в Awake и отписываем его в OnDestroy. В таком случае можно было бы обойтись одним binds, не очищая его никогда. Каждый объект подписывается и отписывается в рамках своего жизненного цикла и разве что при переходе между сценами происходило бы немного лишней работы над поштучным отписыванием выгружаемых объектов. Но такой подход не прощает ошибок, выгруженный подписчик в лучшем случае сразу сломает вызов делегата и будет найдена, а в худшем - станет причиной утечки памяти. Так что, в качестве "защиты от дурака" лучше при переходе явно отписывать все, что не было обозначено как Global.

Итак, интерфейс EventManager cводится к 5 методам:

        public void Bind<T>(string name, Action<T> ev)        public void BindGlobal<T>(string name, Action<T> ev)        public void Unbind<T>(string name, Action<T> ev)        public void UnbindGlobal<T>(string name, Action<T> ev)                  public void Bind(string name, Action ev)        public void BindGlobal(string name, Action ev)        public void Unbind(string name, Action ev)        public void UnbindGlobal(string name, Action ev)                  public void Invoke<T>(string name, T arg)                  public void Invoke(string name)

Мы можем подписываться на события и отправлять их. И все это с аргументом произвольного типа. В примере из статьи про FSM мы передавали ввод с джойстика в автомат и, если состояние предусматривает такую возможность, передавали в EventManager событие изменения положения джойстика , на которое может подписаться компонент, управляющий положением игрока(Или потомок MonoBehaviourPro, какой нибудь PlayerController, который передаст информацию о вводе в свой автомат, и если игрок в состоянии SPlayerDriving , будет передавать ввод с джойстика уже автомобилю, за рулем которого он сидит, а если в SPlayerClimbing, джойстик будет двигать игрока перпендикулярно нормали плоскости, по которой он движется, с соответствующей анимацией. Но это уже более сложные примеры, не будем на этом задерживаться). Или же, на входе в состояние игры SWin мы можем отправить событие level_done, а на него подписать анимацию экрана победы, конфетти, и чего там еще ваш ГД придумает.

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

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

Эта статья - вторая в серии:
- Разделяй и властвуй Использование FSM в Unity
- Подпишись, чтобы не пропустить События

Подробнее..

Хочу больше годных профстатей, Хабр

21.06.2021 10:15:25 | Автор: admin

Листая страницы Хабра, поймал себя на мысли, что я воспринимаю Хабр как новостную ленту в социальной сети. То есть как нечто, что прямого отношения лично ко мне не имеет и касается меня очень косвенным путем. Нечто полуразвлекательное-полупознавательное.

Ну, судите сами. Вот примерный список тем, которые превалируют на Хабре.

  1. Что там новенького у Илона Петровича Маска.

  2. Как с помощью Arduino, говна и палок сделать годный фаллоимитатор радиоприемник.

  3. Как я ушел с прошлой работы, и как мне было там плохо.

  4. Как я нашел свою текущую работу, и какая она крутая.

  5. Как живется специалисту X в стране Y.

  6. Какой путь нужно проделать фельдшеру из Ангарска, чтобы стать тестировщиком мобильных приложений в Ирландии.

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

  8. Промываем косточки крупным компаниям.

  9. Исторические экскурсы в IT/технологии/медицину.

  10. Реклама компаний.

  11. Мнения обо всем отвлеченном на свете.

  12. И т. д.

Все эти темы и все статьи неплохие, интересные. Но я хотел бы другого.

Я давно программирую на С++/qt. Думаю, что могу создать с нуля любой программный продукт (desktop) . Могу набирать команды, могу выстраивать отношения с заказчиками и т.д. Периодически приходится запускать (создавать) новые направления/программные продукты. Однако время, затрачиваемое мной и командой на каждый новый продукт, остается примерно постоянным. Вернее, не совсем так. Время суммарной работы оказывается в прямой пропорциональной зависимости от сложности продукта (объема кодовой базы). То есть постоянной величиной оказывается производительность работ, или эффективность труда. На всякий случай оговорюсь, что речь не идет о новичках, в команде только опытные толковые сотрудники.

Так вот, эту самую производительность труда очень хотелось бы увеличить. Как и в чем мне может помочь Хабр? Или я зря надеюсь?

Мне хотелось бы поднимать свой профессиональный уровень серьезными профессиональными статьями по проектированию/созданию/ведению больших продуктов. Статьями, которые по легкости восприятия были бы такого же классного уровня, как и сейчас большинство статей на Хабре. Но статьями, которые по глубине и полезности были бы как классические книги Скотта Майерса, банды четырех, Алана Купера, Роберта Мартина и др. Знаете, читая эти книги, я прибавлял каждый раз в квалификации. К сожалению, читая статьи на Хабре, я этого не чувствую. Даже более того: не могу припомнить случая, когда я хотел изучить какой-то новый для меня (и обычно нетривиальный) нюанс и находил бы его на Хабре. Я находил его где угодно, но только не на Хабре. Или вообще не находил.

Посему я очень жду и буду приветствовать появление на Хабре статей по следующим направлениям.

Новые шаблоны проектирования (С++)

Да, я знаю, что шаблоны не догма и не панацея, и всё всегда можно придумать и самому. Но я также знаю, что это проверенная годами экономия времени архитекторов и программистов. Это кругозор, который (при его наличии) позволяет делать сложную работу быстрее, а то и вообще моментально. У меня сложилось ощущение, что в мире С++ развитие шаблонов практически остановилось на известной книге банды четырех. Там описано 23 шаблона, и еще примерно столько же можно накопать в интернете. Я хочу больше! В моей практике были случаи, когда приходилось создавать шаблоны, разительно непохожие на известные. И приходилось тратить довольно много времени на приведение их к товарному использованию, хотя бы даже на продумывание такой терминологии, которая бы естественно бы воспринималась коллегами. Уверен, что если бы мы имели возможность в нужный момент найти и прочитать описание свежего шаблончика, наша работа местами была бы намного быстрее.

Думаю, на Хабре замечательно бы смотрелись статьи в классическом стиле банды четырех: описание и идеология шаблона, пример реальной задачи, код С++, границы применения.

Кстати, по шаблонам есть фундаментальный труд POSA: 5-томник на 2000+ страниц, перевода на русский язык до сих пор нет. Чем не непаханное поле?

Шаблоны ведения проектов в Git

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

  1. Я веду маленький проект на 20 тысяч строк кода, в нем задействовано 5 человек, с системой контроля версий мы работаем вот так (и описать конкретно, вплоть до команд командной строки).

  2. Я веду неплохой проект на 100 тысяч строк кода, в нем задействовано 10 человек, и мы работаем вот так: схемы, команды git.

  3. Мы с тремя командами по 10 человек развиваем проект на 1 миллион строк кода, продаем продукт разным клиентам в разных конфигурациях, и всё свое хозяйство мы покрыли регрессионным тестированием. И для этого мы делаем вот это: схемы, команды git.

  4. У нас работает 200 человек, и у нас 10 миллионов строк кода в монорепе на 5 продуктов, и каждый продукт ещё поставляется в трех разных версиях. Мы опытным путем пришли, что только так: схемы, команды git.

  5. А у нас очень много кода и много микросервисов, каждый в своем репозитории, впрочем, есть и подрепозитории (подмодули). Часть кода покрыта тестами, часть нет. Раньше мы работали вот так, а теперь перешли на вот это (схемы, команды), но по эффективности труда одинаково приемлемо.

GUI

Дизайн GUI, новые подходы, примеры удачных и неудачных виджетов, примеры плохих и хороших интерфейсов/решений. Эта область очень динамично развивается в плане того, что мы видим на экранах своих смартфонов и мониторов (причем на смартфонах более динамично, чем на мониторах). Однако в плане научного описания, осмысления и систематизации всё как-то вяло. То есть решений полно, но ими никто не делится. А если и делится, то как-то не очень доходит до конкретных исполнителей, и те пишут с нуля. Думаю, ситуация бы улучшилась, если бы появилась серия статей в таком духе:

  1. Камрады, я внедрил к себе гамбургер-меню на 500 строк (qt) вот отсюда (ссылка). Вот, смотрите: скриншот, gif-анимация. Работает чётко! Лицензия LGPL. Короче, пользуйтесь все.

  2. Я создал свой виджет ввода паролей. Он круче, чем другие по таким-то причинам. Делюсь! Ссылка на репозиторий, скриншоты.

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

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

  5. Раньше у нас был такой интерфейс, и такие задачи. А потом добавилась еще одна, и мы в корне переделали интерфейс. Рассказываю, почему прошлый интерфейс был оптимален, а текущий супероптимален.

  6. Я создал свою библиотеку виджетов, да еще с библиотекой стилей. Ссылка. Область применения такая-то. Могу продать хорошим людям (или подарить).

  7. Я супермегадизайнер, и на примере 30 известных приложений за последний год объясню вам, что попало в тренд, что не попало, а что создает будущий тренд.

Успешный опыт решения важных и нетривиальных задач из самой близкой действительности

Знаете, меня не сильно цепляют новости, о том, какие планы у Джеффа Безоса на космос или как Boston Dynamics обучает своего пса. Это не увеличивает мою зарплату. Я хочу чего-то более близкого и понятного мне, но самое главное применимого в моей работе.

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

Сложные ли это задачи? Непростые, но решаемые. И некоторые из них решены многократно самыми разными производителями софта.

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

Да, я знаю, что есть такие понятия, как коммерческие секреты, патенты и всякое такое. Но ведь несложно же из груды решенных задач вытащить что-то такое, что давно уже не представляет коммерческой ценности и поделиться этим с программистским сообществом. Подозреваю, что в команде разработки, к примеру, Adobe Photoshop найдется немалая кипа примеров изящно решенных нетривиальных задач, которые незаслуженно скрыты от мира. И если бы их открыть в виде конкретных формул, диаграмм (а в идеале исходного кода) и разместить в удобочитаемом виде на Хабре, это было бы очень многим полезно.

Вместо послесловия

Изложенные мной хотелки уже присутствуют в некотором небольшом количестве на Хабре, а помимо этого, подобную информацию по крупинкам можно собирать на конференциях, искать в книгах и некоторых научных журналах. Но трудозатраты слишком велики! Бывает, читаешь книгу в 500 страниц, чтобы найти хоть намек на решение, а его там не оказывается. Или, того хуже, оказывается, но такой, что лучше бы его не было Статьи в научных журналах часто оказываются написанными не с целью поделиться со всем миром своими достижениями, а просто формально отчитаться за очередной грант. Да и вообще, кажется, что печатные научные журналы по программированию эволюционно проигрывают интернет-платформам.

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

Подробнее..

Категории

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

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru