Читайте также:
|
|
virtual ProgramNode* NewReturnStatement(
ProgramNode* value) const;
virtual ProgramNode* NewCondition(
ProgramNode* condition,
ProgramNode* truePart, ProgramNode* falsePart) const;
ProgramNode* GetRootNode (); private:
ProgramNode* _node; i.
Дерево разбора состоит из экземпляров подклассов класса ProgramNode, таких как StatementNode, ExpressionNode и т.д. Иерархия классов ProgramNode — это
пример паттерна компоновщик. Класс ProgramNode определяет интерфейс для манипулирования узлом программы и его потомками, если таковые имеются:
class ProgramNode { public:
// манипулирование узлом программы
virtual void GetSourcePosition(int& line, int& index);
// манипулирование потомками virtual void Add(ProgramNode*); virtual void Remove(ProgramNode*);
virtual void Traverse(CodeGeneratork); protected:
ProgramNode();
Операция Traverse (обход) принимает объект CodeGenerator (кодогенератор) в качестве параметра. Подклассы ProgramNode используют этот объект для генерации машинного кода в форме объектов Bytecode, которые помещаются в поток BytecodeStream. Класс CodeGenerator описывается паттерном посетитель:
class CodeGenerator { public:
virtual void Visit(StatementNode*);
virtual void Visit(ExpressionNode*);
protected:
CodeGenerator(BytecodeStreamk); protected:
BytecodeStreamk _output;
Паттерн Facade
У CodeGenerator есть подклассы, например StackMachineCodeGenerator
и RISCCodeGenerator, генерирующие машинный код для различных аппаратных архитектур.
Каждый подкласс ProgramNode реализует операцию Traverse и обращается к ней для обхода своих потомков. Каждый потомок рекурсивно делает то же самое для своих потомков. Например, в подклассе ExpressionNode (узел выражения) операция Traverse определена так:
void ExpressionNode::Traverse (CodeGenerator& eg) { eg.Visit(this);
ListIterator<ProgramNode*> i(_children);
for (i. First ();! i. IsDone (); i.NextO) { i.Currentltem()->Traverse(eg);
Классы, о которых мы говорили до сих пор, составляют подсистему компиляции. А теперь введем класс Compiler, который будет служить фасадом, позволяющим собрать все эти фрагменты воедино. Класс Compiler предоставляет простой интерфейс для компилирования исходного текста и генерации кода для конкретной машины:
class Compiler { public:
Compiler();
virtual void Compile(istream&, BytecodeStream&);
void Compiler::Compile (
istream& input, BytecodeStreamk output) {
Scanner scanner(input); ProgramNodeBuilder builder; Parser parser;
parser.Parse(scanner, builder);
RISCCodeGenerator generator(output); ProgramNode* parseTree = builder.GetRootNode(); parseTree->Traverse(generator); }
В этой реализации жестко «зашит» тип кодогенератора, поэтому программисту не нужно явно задавать целевую архитектуру. Это может быть вполне разумно, когда есть всего одна такая архитектура. Если же это не так, можно было бы изменить конструктор класса Compiler, чтобы он принимал объект CodeGenerator в качестве параметра. Тогда программист указывал бы, каким генератором пользоваться при
Структурные паттерны
инстанцировании объекта Compiler. Фасад компилятора можно параметризовать и другими участниками, скажем, объектами Scanner и ProgramNodeBuilder, что повышает гибкость, но в то же время сводит на нет основную цель фасада - предоставление упрощенного интерфейса для наиболее распространенного случая.
Известные применения
Пример компилятора в разделе «Пример кода» навеян идеями из системы компиляции языка ObjectWorks\Smalltalk [РагЭО].
В каркасе ЕТ++ [WGM88] приложение может иметь встроенные средства инспектирования объектов во время выполнения. Они реализуются в отдельной подсистеме, включающей класс фасада с именем ProgrammingEnvironment. Этот фасад определяет такие операции, как InspectObject и InspectClass для доступа к инспекторам.
Приложение, написанное в среде ЕТ++, может также запретить поддержку инспектирования. В таком случае класс ProgrammingEnvironment реализует соответствующие запросы как пустые операции, не делающие ничего. Только подкласс ETProgrammingEnvironment реализует эти операции так, что они отображают окна соответствующих инспекторов. Приложению неизвестно, доступно инспектирование или нет. Здесь мы встречаем пример абстрактной связанности между приложением и подсистемой инспектирования.
В операционной системе Choices [CIRM93] фасады используются для составления одного каркаса из нескольких. Ключевыми абстракциями в системе Choices являются процессы, память и адресные пространства. Для каждой из них есть соответствующая подсистема, реализованная в виде каркаса. Это обеспечивает поддержку переноса Choices на разные аппаратные платформы. У двух таких подсистем есть «представители», то есть фасады. Они называются FileSystemlnterface (память) и Domain (адресные пространства).
Паттерн Flyweight
Например, для каркаса виртуальной памяти фасадом служит Domain. Класс Domain представляет адресное пространство. Он обеспечивает отображение между виртуальными адресами и смещениями объектов в памяти, файле или на устройстве длительного хранения. Базовые операции класса Domain поддерживают добавление объекта в память по указанному адресу, удаление объекта из памяти и обработку ошибок отсутствия страниц.
Как видно из вышеприведенной диаграммы, внутри подсистемы виртуальной памяти используются следующие компоненты:
Q MemoryObject представляет объекты данных;
a MemoryObj ectCache кэширует данные из объектов MemoryObj ects в физической памяти. MemoryObj ectCache - это не что иное, как объект Стратегия, в котором локализована политика кэширования;
a AddressTranslat ion инкапсулирует особенности оборудования трансляции адресов.
Операция RepairFault вызывается при возникновении ошибки из-за отсутствия страницы. Domain находит объект в памяти по адресу, где произошла ошибка и делегирует операцию RepairFault кэшу, ассоциированному с этим объектом. Поведение объектов Domain можно настроить, заменив их компоненты.
Дата добавления: 2015-09-11; просмотров: 96 | Поможем написать вашу работу | Нарушение авторских прав |