Глоссарий 3 страница

Тема 6. Определение инструментов разработки. Системные макросы и их применение в текстах разработки. Инструментальные средства и технологии Windows. MFC. SDK

 

Рекомендации по стилю программирования:

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

Сходным объектам давайте сходные имена, которые бы показывали их сходство и подчеркивали различия между ними

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

Будьте точны.

Форматируйте код, подчеркивая его структуру

Используйте естественную форму выражений

Используйте скобки для устранения неясностей

Разбивайте сложные выражения

Будьте проще

Будьте осторожны с побочными эффектами

Будьте последовательны в применении отступов и фигурных скобок

Используйте идиомы (устоявшиеся приемы) для единства стиля

Используйте цепочки else-if для многовариантных ветвлений

Используйте символьные, а не целые константы

Используйте средства языка для определения размера объекта (Не используйте явно заданного размера ни для каких типов данных)

Не пишите об очевидном

Комментируйте функции и глобальные данные

Не комментируйте плохой код, а перепишите его

Вносите ясность, а не сумятицу

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

Однако стоит ли так беспокоиться о стиле? Кому какая разница, как программа выглядит изнутри, если она работает? Не слишком ли много времени придется тратить на ее "причесывание"? Не слишком ли расплывчаты рекомендуемые правила?

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

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

Тема 7. Средства визуального программирования - MS Visual Studio, Borland Delphi и др.

 

Целью тестирования является обнаружение ошибок в программе. В тестирование входят следующие этапы:

а) постановка задачи для теста, б) проектирование тестов,

в) написание тестов, г) тестирование тестов, д) выполнение тестов, е) изучение результатов тестирования.

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

На этом этапе выделяют две задачи:

) определение природы ошибки;

) исправление ошибки. Наиболее распространенными и наименее эффективными для отладки являются так называемые методы ‘грубой силы’. К ним относят:

) отладку с использованием дампа памяти;

) отладку с использованием операторов печати по всей программе;

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

. метод индукции.

. метод дедукции

Из всех этапов проектирования логики программных модулей этап отладки является наименее формализованным. В нем выделяют две задачи:

определение природы ошибки;

исправление ошибки.

Стратегии тестирования.

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

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

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

разрабатывать тесты, используя методы стратегии "черного ящика”;

дополнительное тестирование, используя методы стратегии "белого ящика”.

Методы "белого ящика":

покрытия операторов (каждый оператор программы выполняется хотя бы 1 раз),

покрытия решений или переходов (каждое направление перехода должно быть реализовано по крайней мере один раз),

покрытия условий (все возможные результаты каждого условия в решении выполнить хотя бы 1 раз),

критерий решений,

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

Методы "черного ящика":

эквивалентное разбиение,

анализ граничных значений,

тестирования таблицы решений (ТР),

тестирование модульных программ.

Метод эквивалентного разбиения.

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

а) каждый тест должен включать столько различных входных условий, сколько это возможно, с тем чтобы минимизировать общее число тестов;

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

а) выделение класса эквивалентности;

б) построение тестов.

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

 

Входные условия Правильные классы эквивалентности Неправильные классы эквивалентности

 

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

Следующий этап данного метода - построение тестов.

Этот процесс включает в себя:

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

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

запись тестов, каждый из которых покрывает один и только один из непокрытых неправильных классов эквивалентности.

Тестирование модульных программ.

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

. восходящее тестирование;

. нисходящее тестирование.

 

Методика восходящего тестирования включает следующие шаги: а) сначала тестируются ‘листья’ дерева структуры программы, т.е. модули Е,C,F. Для их тестирования программируются драйверы. В функции драйвера входит формирование тестовых данных для отлаживаемого модуля и передача ему управления; б) затем аналогично тестируются модули вышележащего уровня совместно с уже оттестированными модулями нижележащего уровня. Применительно к рассматриваемому нами примеру проектируются драйверы для тестирования пар B-E и D-F; в) пошаговый процесс продолжается до тех пор, пока не будет включен в процесс тестирования последний модуль (‘корень’ дерева структуры программы). Для нашего примера это будет модуль А. Для его тестирования совместно с вызываемыми программами нижележащего уровня драйвер разрабатывать не требуется.

Альтернативное нисходящее тестирование состоит из следующих действий: а) тестирование начинается с вызывающего модуля программы (‘корня’ дерева структуры программы). Для модулей нижележащего уровня (вызываемых) программируются так называемые ‘заглушки’; б) после переходят к тестированию нижележащих модулей. Причем, формализованной процедуры подключения к вызывающему модулю нижележащих вызываемых модулей не существует. Единственное ограничение, которым руководствуются при выборе очередного претендента на тестирование, заключается в том, что этот модуль должен вызываться уже оттестированным модулем вышележащего уровня. Если очередной тестируемый модуль вызывает модули еще нижележащего уровня, для нижележащих модулей программируются ‘заглушки’; в) процесс тестирования продолжается до тех пор, пока не будет оттестирован последний модуль из ‘листьев’ дерева структуры программы.

Тема 8. Средства визуального программирования. Результаты компиляции. Список опций компилятора и компоновщика. Управление компилятором (С++Builder)

 

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

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

Бурное развитие технологий программирования, основанных на объектном подходе, позволило решить многие проблемы. Так, были созданы среды, поддерживающие визуальное программирование, например, Delphi, C++ Builder, Visual C++ и т.д. При использовании визуальной среды у программиста появляется возможность проектировать некоторую часть, например, интерфейсы будущего продукта, с применением визуальных средств добавления и настройки специальных библиотечных компонентов. Результатом визуального проектирования является заготовка будущей программы, в которую уже внесены соответствующие коды.

Использование объектного подхода имеет много преимуществ, однако его конкретная реализация в объектно-ориентированных языках программирования, таких как Pascal и C++, имеет существенные недостатки: фактически отсутствуют стандарты компоновки двоичных результатов компиляции объектов в единое целое даже в пределах одного языка программирования: компоновка объектов, полученных разными компиляторами C++ в лучшем случае проблематична, что приводит к необходимости разработки программного обеспечения с использованием средств и возможностей одного языка программирования высокого уровня и одного компилятора, а значит, требует наличия исходных кодов используемых библиотек классов; изменение реализации одного из программных объектов, как минимум, связано с перекомпиляцией соответствующего модуля и перекомпоновкой всего программного обеспечения, использующего данный объект.

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

Тема 9. Построение интерфейса программы. Принципы разработки инструментария

 

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

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

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

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

Визуальные диаграммы чаще всего строятся с помощью унифицированного языка моделирования UML (Unified Modeling Language). Его стандарт разработан группой OMG (Object Management Group, версии языка в 1997-первая, 2004-последняя) для задач объектно-ориентированного проектирования.

Существуют множество диаграмм в языке UML, но наиболее полезными при проектировании объектно-ориентированной программы являются:

диаграммы вариантов использования

диаграммы классов

диаграммы последовательности.

Далее рассмотрим эти виды диаграмм на примерах.

Диаграммы вариантов использования:

 

 

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

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

Диаграммы показывают, какие действующие лица инициируют варианты использования. Из них также видно, когда действующее лицо получает информацию от варианта использования. В сущности диаграмма вариантов использования может иллюстрировать требования к системе. В нашем примере клиент банка инициирует различные варианты использования: "Снять деньги со счета", "Перевести деньги", "Положить деньги на счет", "Показать баланс", "Изменить идентификационный номер", "Произвести оплату". Банковский служащий может инициировать вариант использования "Изменить идентификационный номер". От варианта использования "Произвести оплату" идет стрелка к Кредитной системе. Действующими лицами могут быть и внешние системы, в данном случае Кредитная система показана именно как действующее лицо - она является внешней для системы ATM. Стрелка, направленная от варианта использования к действующему лицу, показывает, что вариант использования предоставляет некоторую информацию действующему лицу. В данном случае вариант использования "Произвести оплату" предоставляет Кредитной системе информацию об оплате по кредитной карточке. Из диаграмм вариантов использования можно получить довольно много информации о системе. Этот тип диаграмм описывает общую функциональность системы. Пользователи, менеджеры проектов, аналитики, разработчики, специалисты по контролю качества и все, кого интересует система в целом, могут, изучая диаграммы вариантов использования, понять, что система должна делать.

Диаграммы последовательности.

 

 

Диаграммы последовательности отражают поток событий, происходящих в рамках варианта использования. Например, вариант использования "Снять деньги" предусматривает несколько возможных последовательностей: снятие денег, попытка снять деньги при отсутствии их достаточного количества на счету, попытка снять деньги по неправильному идентификационному номеру и некоторые другие. Нормальный сценарий снятия $20 со счета (при отсутствии таких проблем, как неправильный идентификационный номер или недостаток денег на счету) показан на рис. В верхней части диаграммы показаны все действующие лица и объекты, требуемые системе для выполнения варианта использования "Снять деньги". Стрелки соответствуют сообщениям, передаваемым между действующим лицом и объектом или между объектами для выполнения требуемых функций. Следует отметить также, что на диаграмме последовательности показаны именно объекты, а не классы. Классы представляют собой типы объектов. Объекты конкретны; вместо класса Клиент на диаграмме последовательности представлен конкретный клиент Джо.

Вариант использования начинается, когда клиент вставляет свою карточку в устройство для чтения - этот объект показан в прямоугольнике в верхней части диаграммы. Он считывает номер карточки, открывает объект "счет Джо" и инициализирует экран ATM. Экран запрашивает у Джо его регистрационный номер. Клиент вводит число 1234. Экран проверяет номер у объекта "счет Джо" и обнаруживает, что он правильный. Затем экран предоставляет Джо меню для выбора, и тот выбирает пункт "Снять деньги". Экран запрашивает, сколько он хочет снять, и Джо указывает $20.

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

Итак, данная диаграмма последовательности иллюстрирует последовательность действий, реализующих вариант использования "Снять деньги со счета" на конкретном примере снятия клиентом Джо $20. Глядя на эту диаграмму, пользователи знакомятся со спецификой своей работы. Аналитики видят последовательность (поток) действий, разработчики - объекты, которые надо создать, и их операции. Специалисты по контролю качества поймут детали процесса и смогут разработать тесты для их проверки. Таким образом, диаграммы последовательности полезны всем участникам проекта.

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

 

Диаграммы классов отражают взаимодействие между классами системы. Например, "счет Джо" - это объект. Типом такого объекта можно считать счет вообще, т.е. "Счет" - это класс. Классы содержат данные и поведение (действия), влияющее на эти данные.

Так, класс Счет содержит идентификационный номер клиента и проверяющие его действия. На диаграмме классов класс создается для каждого типа объектов из диаграмм последовательности или Кооперативных диаграмм. Диаграмма классов для варианта использования "Снять деньги" показана на рис.

На диаграмме показаны связи между классами, реализующими вариант использования "Снять деньги". В этом процессе задействованы четыре класса: Card Reader (устройство для чтения карточек), Account (счет), ATM (экран ATM) и Cash Dispenser (кассовый аппарат). Каждый класс на диаграмме классов изображается в виде прямоугольника, разделенного на три части. В первой части указывается имя класса, во второй - его атрибуты. Атрибут - это некоторая информация, характеризующая класс.

Например, класс Account (счет) имеет три атрибута: Account Number (номер счета), PIN (идентификационный номер) и Balance (баланс). В последней части содержатся операции класса, отражающие его поведение (действия, выполняемые классом). Связывающие классы линии показывают взаимодействие между классами.

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

Тема 10. Отладка программ. Инструменты. Методика отладки

 

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

Программная система может состоять из отдельных, разработанных разными организациями выполняемых программ; из программ, обменивающихся данными через порты; из отдельных резидентных программ.

Традиционно программа проектировалась из отдельных файлов, модулей или классов, которые компилировались и компоновались в единое целое.

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

Тема 11. Тестирование. Разработка инвариантов и тестовых примеров

 

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

Компонентный подход лежит в основе технологий, разработанных на базе COM (Component Object Model - компонентная модель объектов), и технологии создания распределенных приложений CORBA (Common Object Request Broker Architecture - общая архитектура с посредником обработки запросов объектов). Эти технологии используют сходные принципы и различаются лишь особенностями их реализации.

Технология СОМ фирмы Microsoft является развитием технологии OLE (Object Linking and Embedding - связывание и внедрение объектов), которая использовалась в ранних версиях Windows для создания составных документов. Технология СОМ определяет общую парадигму взаимодействия программ любых типов: библиотек, приложений, операционной системы, т.е. позволяет одной части программного обеспечения использовать функции (службы), предоставляемые другой, независимо от того, функционируют ли эти части в пределах одного процесса, в разных процессах на одном компьютере или на разных компьютерах (рис.1.8). Модификация СОМ, обеспечивающая передачу вызовов между компьютерами, называется DCOM (Distributed COM - распределенная СОМ).

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

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

Каждый интерфейс имеет имя, начинающееся с символа I и глобальный уникальный идентификатор IID (Interface IDentifier). Любой объект СОМ обязательно реализует интерфейс (Unknown (на схемах этот интерфейс всегда располагают сверху). Использование этого интерфейса позволяет получить доступ к остальным интерфейсам объекта. Объект всегда функционирует в составе сервера - динамической библиотеки или исполняемого файла, которые обеспечивают функционирование объекта. Различают три типа серверов: внутренний сервер: реализуется динамическими библиотеками, которые подключаются к приложению-клиенту и работают в одном с ними адресном пространстве. Это наиболее эффективный сервер, кроме того, он не требует специальных средств; локальный сервер: создается отдельным процессом (ехе), который работает на одном компьютере с клиентом; удаленный сервер: создается процессом, который работает на другом компьютере.