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


Мы поможем в написании ваших работ!



ЗНАЕТЕ ЛИ ВЫ?

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



Созданный и отлаженный модуль не изменяется.

Расширение программ обеспечивается механизмами расширения.

Принципы расширяемого программирования

Сторонний разработчик добавляет функционал:

1) без привлечения разработчиков;

2) без модификации исходных кодов;

3) без перекомпиляции, переустановки и перезагрузки;

Без согласования с другими разработчиками.

Примеры: MS Word, Visual Studio, Eclipse, IE, OS (drivers, services…)

Применение методологии объектно-ориентированного программирования для построения расширяемых программ

Желательно встроить средства расширения в язык.

Расширение:

1) кода – новый модуль изменяет работу процедур существующих модулей;

2) данных – новый модуль добавляет к существующим объектам новые свойства;

Расширение кода.

Постановка задачи: Существуют модули A, B, C. В и С импортируют А. В В и С нужно изменить работу модуля А, если А изменять нельзя, В и С ничего не знают друг о друге.

2) Решения:

А) процедурные переменные – традиционное решение. В ООП процедурные переменные скрываются в таблицах виртуальных методов.

MODULE A

Procedure Insert* (S:string); * – public-процедура

Var InsertEvent*: PROCEDURE (S: string)

MODULE B
import A

Procedure OnInsert(s: string)

Begin End;

Проблемы: 1) затраты на дизайн; (register/unregister для поддержки некоторых обработчиков, возможость отметы вставки, синхронизация при доступе из разных потоков)

Разрыв контекста (beforeInsert, afterInsert. Перехватываются обе процедуры. А если несколько потоков? Разные процедуры должны работать с 1м контекстом)

3) нарушение инварианта var InsertInProgress (B.Insert – true, A.Insert – false)

В случае исключения переменная InsertInProgress остается в неправильном состоянии. Нарушение инварианта – следствие нарушения контектса.

Скрытая рекурсия.

Insert* (s: string)

Begin

InsertIvent(S) – м.б. неявная рекурсия

End;

Рекурсия м.б. недопустима, но компилятор обладает недостаточной информацией. A.Insert(s).

Выгрузка модулей. Если модуль нужно выгрузить, возможно ли дать ОС команду «выгрузить модуль»? Нет, т.к. он может иметь ссылки с других модулей. Сама система не может удалить модель, не имеет права его выбросить.

Решение всех указанных проблем – переопределение процедур.

MODULE A; MODULE B

PROCEDURE Insert* (S:string); IMPORT A;

BEGIN PROCEDURE A.Insert+ (S:string);

… BEGIN

END; …

… BASE(s);

END A. …

END;

END B.

Реализация: 1) перенастройка адресов (при загрузке модуля реализации В. Старое значение нужно сохранить в переменной);

2) вызов через процедурную переменную – для всех процедур модуля объявляется неявная переменная, модуль переопределяет процедуры;

3) процедура-переходник. Делается вызов процедуры-переходника, которая вызовет функцию через процедурную переменную, переходник сделает jmp. Перехват – изменение переменных, через которые делается вызов;

Замена кода процедуры по месту. В 1х инструкциях сделать jmp на свою процедуру.

5).NET компиляция происходит в промежуточный язык (полностью лишен недостатков). За счет представления программы в промежуточном языке можно осуществить перехват.

Минусы:

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

2) все вызовы косвенные – потеря производительности;

3) всегда есть потеря на jmp;

Требует генерации специального кода компилятором. Процедура не м.б. слишком короткой (только ret на перехват).


В современных микропроцессорах за счет кэширования производительность увеличивается.


Расширение данных

Задача: модули А, В, С. В и С импортируют А. А имеет множество объектных типов. При загрузке В и С требуется доопределить поля данных в объектах, не приостанавливая их работу. 3 разных производителя, А не дает модифицировать код, В и С не знают друг о друге.

MODULE Network

TYPE

Socket* = POINTER TO RECORD

END;

PROCEDURE Send* (S: Socket; Data: String)

BEGIN

END

END Network;

Module NetworkMonitor

Import Network;

TYPE SocketStat = POINTER TO RECORD

CurrentSpeed: INTEGER;

END;

PROCEDURE Network Send+ (S: Network.Socket; Data: String);

Var Stat: SocketStat;

BEGIN

BASE(S, Data);

Stat:= FindSocketStat(S);

Stat.CurrentSpeed:= …;

END;

END NetworkMonitor;

TYPE SocketStat = POINTER TO RECORD (Socket)

END;

PROCEDURE … (S:Network.Socket; Data: String);

BEGIN

BASE(S,Data);

S(SocketStat).CurrentSpeed:= …;
END;

В модуле Network создаются экземпляры типа Socket, а не SocketStat. Чтобы создать экземпляр нового типа, нужно использовать паттерны (Factory Pattern).

Если расширяющих модулей несколько и все они создают свои типы на основе типа Socket, то Factory Method не работает.

Ассоциирование данных

MODULE NetworkMonitor;

Var SocketStatCollection: …;

PROCEDURE Network Send+ (S, Data)

BEGIN

BASE(S, DATA);

FindSocketStat(S).CurrentSpeed;
END;

Минусы: высокая трудоемкость (поддерживать ассоциацию при create и удалении первичных объектов). При уничтожении socket нужно уничтожить socketstat. => снижение надежности, уменьшение производительности, необходимость синхронизации – при доступе к таблицам поиска необходимо синхронизировать потоки;

РЕШЕНИЕ: дополнение типов данных.

MODULE NetworkMonitor;

IMPORT Network;

TYPE

SocketStat = POINTER TO RECORD EXTENSION Network.Socket

END;

PROCEDURE NetworkSend+ (…)

BEGIN
BASE(S,Data);

S[SocketStat].CurrentSpeed;

END;

Реализация: 1) дополнение расширяет все экземпляры определенного типа данных;

2) память под дополнение выделяются на летупри первом обращении к объекту;

3) дополнения можно использовать только для данных, которые находятся в динамической памяти;

4) ортогональны «наследованию», могут создаваться в любом количестве и для любого типа данных в иерархии;

Эти методы позволяют:

1) упростить создание, сопровождение и развитие расширяемых систем;

2) повысить надежность;

3) повысить производительность;

Обеспечить статическую и динамическую верификацию правил расширения системы.

АОП – принципы программирования. АОП зависит от ООП.

Oberon

Oberon: разрабатывался для создания маленьких систем управления, ОС. Oberon System для Lilith PC.

Понятие объекта не вводится, но свой язык Вирт называет ООП-языком.

Главное свойство – свойство расширяемости (наследование типов, но виртуальные методы отсутствуют. Есть записи и их наследование.)

Boolean, char, integer, real, longint, set (0..31)

ArrayType = ARRAY length(“,” length) of type – нужно явно указывать длину массива, динамических массивов нет. Нумерация массивов с 0.

RecordType = RECORD [“(BaseType)”] [Fields] end;

PointerType = POINTER to type; Только для записей и массивов. Нельзя преобразовать указатель в число, но есть функция addr, возвращающая int. Число нельзя интерпретировать как указатель.

Система Oberon имеет сборщик мусора.

Процедурная переменная – указатель на функцию.

ProcedureType = PROCEDURE[formal parameters].

“=” | “#” (не равно) | “<” | “<=” | “>” | “>=” | “IN” | “IS”

“+” “-” “OR”

“*” “/” “DIV” “MOD” “&”

If (ch>=”A”) then

elsif (ch>=”0”) then

elsif (ch=22x) then ReadStr.

End.

CASE k of

0:

|1:

|2:

End.

Компилятор оптимизирует, делая goto по таблице, где индексы – ключ k.

Oberon минималистичен – из него нельзя ничего выбросить.

WHILE j>0 DO

j:=j DIV 2;

i:=i+1;

END;

While m>n DO m:=m – n

Elsif n>m DO n:=n-m

REPEAT

UNTIL expression (условие выхода из цикла)

For v:=beg to en BY inc do s end

Beg, en вычисляются только 1 раз.

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

Существует стандартный модуль System для разработки ОС.

В целях упрощения отказались от обработки исключений.

Component Pascal

ООП + Oberon = Component Pascal

Главная идея уточнений по сравнению с Обероном была в том, чтобы дать проектировщику компонентного каркаса (т.е. интерфейсов модулей, определяющих абстрактные классы для конкретной проблемной области) более полный контроль над ее проектируемыми свойствами в плане безопасности. Введены специальные атрибуты для типов (ABSTRACT, EXTENSIBLE, LIMITED) и методов (ABSTRACT, EMPTY, EXTENSIBLE), позволяя автору программной компоненты (группы модулей) осуществлять контроль в плане того, разрешать или нет модулям-клиентам расширять предлагаемые им типы.

2. Модернизирована несколько устаревшая система основных типов Оберона: теперь набор основных типов Компонентного Паскаля является надмножеством для основных типов языка Java. Основные «рабочие» типы INTEGER, REAL и CHAR соответствуют 32-, 64- (т. н. двойная точность) и 16-(Unicode)-битовым переменным, что позволяет уменьшить разнообразие основных типов, реально используемых в большинстве случаев; использование других типов (LONGINT, SHORTREAL, SHORTCHAR и т. д.) ограничивается специальными приложениями.

3. Добавлены базовые средства для работы с цепочками литер (неявный тип String), что делает Компонентный Паскаль более удобным, чем Паскаль или Оберон, для работы со строками. Цепочки литер представляются массивами литер (ARRAY OF CHAR или ARRAY OF SHORTCHAR), причем значением считается последовательность литер до первого вхождения специальной литеры-ограничителя 0X. Цепочки литер можно сравнивать (подразумевается лексикографическое сравнение) и складывать (конкатенация). Конструкция a:= b$ позволяет скопировать в массив литер a цепочку, хранящуюся в массиве литер b (включая литеру-ограничитель 0X), даже если присваивание a:= b запрещено (например, из-за разной длины массивов a и b).

 

13. Имитация модульного программирования в языке C++. Понятие пространства имен.

 

Имитация модульного программирования в языке C++

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

 

Роль программного интерфейса модуля играет h-файл, а cpp-файл – роль реализации этого модуля.

 

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

Очевидно, что программисту при включении h-файла другого модуля предоставляется выбор: подключить его в h-файле модуля или в cpp-файле. В данном случае предпочтение следует отдавать части реализации модуля (cpp-файл).

При подключении h-файла следует придерживаться следующей схемы: предположим, что наш модуль называется SysModule и состоит из двух частей: SysModule.h и SysModule.cpp.

 

Рекомендуется следующая схема подключения:

SysModule.h:

#include"Config.h" // наш файл конфигурации подключается первым во всех h-файлах всех наших й проектов

#include"Другой стандартный модуль"

#include"Другой наш модуль"

 

SysModule.cpp:

#include"Файл предкомпилированных заголовков"

#include"Еще один наш модуль"

#include"Другой стандартный модуль"

#include"SysModule.h" // подключается последним

 

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

#ifndef __SysModule_h__

#define __SysModule_h__

...

#endif

 

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

 

Согласно стандарту ISO, любой h- и cpp-файл в С++ должен заканчиваться символом перевода строки.



Поделиться:


Последнее изменение этой страницы: 2016-08-14; просмотров: 273; Нарушение авторского права страницы; Мы поможем в написании вашей работы!

infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 18.218.209.8 (0.055 с.)