Главная страница
Навигация по странице:

  • 12.1 Порождающие паттерны проектирования

  • 12.1.1 Абстрактная фабрика

  • 12.1.2 Строитель (Builder)

  • 12.1.3 Фабричный метод (Factory Method)

  • ттттт. Объектноориентированное программирование


    Скачать 1.73 Mb.
    НазваниеОбъектноориентированное программирование
    Анкорттттт
    Дата30.10.2021
    Размер1.73 Mb.
    Формат файлаpdf
    Имя файлаOOP-PrePrint.pdf
    ТипКонспект
    #259341
    страница10 из 15
    1   ...   7   8   9   10   11   12   13   14   15

    11.3 Иерархия классов исключений
    В начале этого раздела мы определили иерархию классов исключений, с помощью которой наша программа сообщает об аномальных ситуациях. В стандартной библиотеке C++ есть аналогичная иерархия, предназначенная для извещения о проблемах при выполнении функций из самой стандартной библиотеки. Эти классы исключений вы можете использовать в своих про- граммах непосредственно или создать производные от них классы для опи- сания собственных специфических исключений.
    Корневой класс исключения в стандартной иерархии называется exception
    . Он определен в стандартном заголовочном файле и является ба- зовым для всех исключений, возбуждаемых функциями из стандартной биб- лиотеки. Класс exception имеет следующий интерфейс: namespace std { class exception public: exception() throw(); exception( const exception & ) throw(); exception& operator=( const exception & ) throw(); virtual

    exception() throw(); virtual const char* what() const throw();
    };

    103
    }
    Как и всякий другой класс из стандартной библиотеки C++, exception помещен в пространство имен std
    , чтобы не засорять глобальное простран- ство имен программы.
    Первые четыре функции-члена в определении класса – это конструктор по умолчанию, копирующий конструктор, копирующий оператор присваи- вания и деструктор. Поскольку все они открыты, любая программа может свободно создавать и копировать объекты-исключения, а также присваивать им значения. Деструктор объявлен виртуальным, чтобы сделать возможным дальнейшее наследование классу exception
    Самой интересной в этом списке является виртуальная функция what()
    , которая возвращает C-строку с текстовым описанием возбужденного ис- ключения. Классы, производные от exception
    , могут заместить what()
    соб- ственной версией, которая лучше характеризует объект-исключение.
    Отметим, что все функции в определении класса exception имеют пу- стую спецификацию throw()
    , т.е. не возбуждают никаких исключений.
    Программа может манипулировать объектами-исключениями (к примеру, внутри catch
    -обработчиков типа exception
    ), не опасаясь, что функции со- здания, копирования и уничтожения этих объектов возбудят исключения.
    Помимо корневого exception
    , в стандартной библиотеке есть и другие классы, которые допустимо использовать в программе для извещения об ошибках, обычно подразделяемых на две больших категории: логические ошибки и ошибки времени выполнения.
    Логические ошибки обусловлены нарушением внутренней логики про- граммы, например логических предусловий или инвариантов класса. Пред- полагается, что их можно найти и предотвратить еще до начала выполнения программы. В стандартной библиотеке определены следующие такие ошибки: namespace std { class logic_error : public exception { // логическая ошибка public: explicit logic_error( const string &what_arg );
    }; class invalid_argument : public logic_error { // неверный аргумент public: explicit invalid_argument( const string &what_arg );
    }; class out_of_range : public logic_error { // вне диапазона public: explicit out_of_range( const string &what_arg );
    }; class length_error : public logic_error { // неверная длина public:

    104 explicit length_error( const string &what_arg );
    }; class domain_error : public logic_error { // вне допустимой области public: explicit domain_error( const string &what_arg );
    };
    }
    Функция может возбудить исключение invalid_argument
    , если полу- чит аргумент с некорректным значением; в конкретной ситуации, когда зна- чение аргумента выходит за пределы допустимого диапазона, разрешается возбудить исключение out_of_range
    , а length_error используется для оповещения о попытке создать объект, длина которого превышает макси- мально возможную.
    Ошибки времени выполнения, напротив, вызваны событием, с самой программой не связанным. Предполагается, что их нельзя обнаружить, пока программа не начала работать. В стандартной библиотеке определены сле- дующие такие ошибки: namespace std { class runtime_error : public exception { // ошибка времени выполнения public: explicit runtime_error( const string &what_arg );
    }; class range_error : public runtime_error { // ошибка диапазона public: explicit range_error( const string &what_arg );
    }; class overflow_error : public runtime_error { // переполнение public: explicit overflow_error( const string &what_arg );
    }; class underflow_error : public runtime_error { // потеря значимости public: explicit underflow_error( const string &what_arg );
    };
    }
    Функция может возбудить исключение range_error
    , чтобы сообщить об ошибке во внутренних вычислениях. Исключение overflow_error гово- рит об ошибке арифметического переполнения, а underflow_error
    – о по- тере значимости.
    Класс exception является базовым и для класса исключения bad_alloc
    , которое возбуждает оператор new()
    , когда ему не удается выделить запро- шенный объем памяти, и для класса исключения bad_cast
    , возбуждаемого в ситуации, когда ссылочный вариант оператора dynamic_cast не может быть выполнен

    105
    Определим оператор operator[]
    в шаблоне
    Array так, чтобы он возбуж- дал исключение типа range_error
    , если индекс массива
    Array выходит за границы:
    #include
    #include template class Array { public:
    // ... elemType& operator[]( int ix ) const
    { if ( ix < 0 || ix >= _size )
    { string eObj =
    "ошибка: вне диапазона в Array::operator[]() "; throw out_of_range( eObj );
    } return _ia[ix];
    }
    // ... private: int _size; elemType *_ia;
    };
    Для использования предопределенных классов исключений в программу необходимо включить заголовочный файл. Описание возбужденного ис- ключения содержится в объекте eObj типа string
    . Эту информацию можно извлечь в обработчике с помощью функции-члена what()
    : int main()
    { try {
    // функция main() такая же, как в разделе 16.2
    } catch ( const out_of_range &excep ) {
    // печатается:
    // ошибка: вне диапазона в Array>elemType>::operator[]() cerr << excep.what() << "\n "; return -1;
    }
    }
    В данной реализации выход индекса за пределы массива в функции try_array()
    приводит к тому, что оператор взятия индекса operator[]()
    класса
    Array возбуждает исключение типа out_of_range
    , которое перехва- тывается в main()

    106
    12.
    ПАТТЕРНЫ ПРОЕКТИРОВАНИЯ
    Паттерны проектирования предназначены для:

    эффективного решения характерных задач проектирования;

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

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

    каждый паттерн описывает решение целого класса проблем;

    каждый паттерн имеет известное имя;

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

    правильно сформулированный паттерн проектирования позволяет, отыскав удачное решение, пользоваться им снова и снова;

    шаблоны проектирования не зависят от языка программирования
    (объектно-ориентированного), в отличие от идиом.
    Идиома (программирование)

    низкоуровневый шаблон проектирова- ния, характерный для конкретного языка программирования.
    Программная идиома

    выражение, обозначающее элементарную кон- струкцию, типичную для одного или нескольких языков программирования.
    12.1 Порождающие паттерны проектирования
    Абстрагируют процесс инстанцирования. Они позволяют сделать си- стему независимой от способа создания, композиции и представления объ- ектов. Шаблон, порождающий классы, использует наследование, чтобы из- менять инстанцируемый класс, а шаблон, порождающий объекты, делеги- рует инстанцирование другому объекту.
    Инстанцирование

    создание экземпляра класса. В отличие от слова «со- здание», применяется не к объекту, а к классу. То есть, говорят: «создать

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

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

    абстрактная фабрика (abstract factory);

    строитель (builder);

    фабричный метод (factory method);

    прототип (prototype);

    одиночка (singleton).
    12.1.1 Абстрактная фабрика
    Абстрактная фабрика (Abstract factory)

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

    108 кнопки). Затем пишутся наследующиеся от него классы, реализующие этот интерфейс.
    Назначение:
    Предоставляет интерфейс для создания семейств взаимосвязанных или взаимозависимых объектов, не специфицируя их конкретных классов
    Плюсы:

    изолирует конкретные классы;

    упрощает замену семейств продуктов;

    гарантирует сочетаемость продуктов.
    Минусы:

    сложно добавить поддержку нового вида продуктов.
    Применение:
    Система не должна зависеть от того, как создаются, компонуются и пред- ставляются входящие в нее объекты; Входящие в семейство взаимосвязан- ные объекты должны использоваться вместе и вам необходимо обеспечить выполнение этого ограничения; Система должна конфигурироваться одним из семейств составляющих ее объектов; требуется предоставить библиотеку объектов, раскрывая только их интерфейсы, но не реализацию.
    #include
    // AbstractProductA class ICar
    { public: virtual void info() = 0;
    };
    // ConcreteProductA1 class Ford : public ICar
    { public: virtual void info()
    { std::cout << "Ford" << std::endl;
    }
    };
    //ConcreteProductA2 class Toyota : public ICar
    { public: virtual void info()
    { std::cout << "Toyota" << std::endl;
    }
    };
    // AbstractProductB class IEngine

    109
    { public: virtual void getPower() = 0;
    };
    // ConcreteProductB1 class FordEngine : public IEngine
    { public: virtual void getPower()
    { std::cout << "Ford Engine 4.4" << std::endl;
    }
    };
    //ConcreteProductB2 class ToyotaEngine : public IEngine
    { public: virtual void getPower()
    { std::cout << "Toyota Engine 3.2" << std::endl;
    }
    };
    // AbstractFactory class CarFactory
    { public:
    ICar* getNewCar()
    { return createCar();
    }
    IEngine* getNewEngine()
    { return createEngine();
    } protected: virtual ICar*createCar()= 0; virtual IEngine*createEngine()= 0;
    };
    // ConcreteFactory1 class FordFactory : public CarFactory
    { protected:
    // from CarFactory virtual ICar* createCar()
    { return new Ford();
    } virtual IEngine* createEngine()
    { return new FordEngine();
    }
    };
    // ConcreteFactory2 class ToyotaFactory : public CarFactory
    { protected:
    // from CarFactory virtual ICar* createCar()
    { return new Toyota();
    }

    110 virtual IEngine* createEngine()
    { return new ToyotaEngine();
    }
    }; int main()
    {
    CarFactory* curFactory = NULL;
    ICar* myCar = NULL;
    IEngine* myEngine = NULL;
    ToyotaFactory toyotaFactory;
    FordFactory fordFactory; curFactory = &toyotaFactory; myCar = curFactory->getNewCar(); myCar->info(); myEngine = curFactory->getNewEngine(); myEngine->getPower(); delete myCar; delete myEngine; curFactory = &fordFactory; myCar = curFactory->getNewCar(); myCar->info(); myEngine = curFactory->getNewEngine(); myEngine->getPower(); delete myCar; delete myEngine; return 0;
    }
    12.1.2 Строитель (Builder)
    Строитель (Builder)

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

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

    изолирует код, реализующий конструирование и представление;

    дает более тонкий контроль над процессом конструирования.
    Применение:

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

    процесс конструирования должен обеспечивать различные представ- ления конструируемого объекта.
    #include
    #include
    #include

    111
    // Product class Pizza
    { private: std::string dough; std::string sauce; std::string topping; public:
    Pizza() { }
    Pizza() { } void SetDough(const std::string& d) { dough = d; }; void SetSauce(const std::string& s) { sauce = s; }; void SetTopping(const std::string& t) { topping = t; } void ShowPizza()
    { std::cout << " Yummy !!!" << std::endl
    << "Pizza with Dough as " << dough
    << ", Sauce as " << sauce
    << " and Topping as " << topping
    << " !!! " << std::endl;
    }
    };
    // Abstract Builder class PizzaBuilder
    { protected: std::auto_ptr pizza; public:
    PizzaBuilder() {} virtual PizzaBuilder() {} std::auto_ptr
    GetPizza() { return pizza; } void createNewPizzaProduct() { pizza.reset (new Pizza); } virtual void buildDough()=0; virtual void buildSauce()=0; virtual void buildTopping()=0;
    };
    // ConcreteBuilder class HawaiianPizzaBuilder : public PizzaBuilder
    { public:
    HawaiianPizzaBuilder() : PizzaBuilder() {}
    HawaiianPizzaBuilder(){} void buildDough() { pizza->SetDough("cross"); } void buildSauce() { pizza->SetSauce("mild"); } void buildTopping() { pizza->SetTopping("ham and pineapple"); }
    };
    // ConcreteBuilder class SpicyPizzaBuilder : public PizzaBuilder
    { public:
    SpicyPizzaBuilder() : PizzaBuilder() {}
    SpicyPizzaBuilder() {} void buildDough() { pizza->SetDough("pan baked"); } void buildSauce() { pizza->SetSauce("hot"); } void buildTopping() { pizza->SetTopping("pepperoni and salami"); }
    };
    // Director class Waiter
    { private:

    112
    PizzaBuilder* pizzaBuilder; public:
    Waiter() : pizzaBuilder(NULL) {}
    Waiter() { } void SetPizzaBuilder(PizzaBuilder* b) { pizzaBuilder = b; } std::auto_ptr
    GetPizza() { return pizzaBuilder->GetPizza(); } void ConstructPizza()
    { pizzaBuilder->createNewPizzaProduct(); pizzaBuilder->buildDough(); pizzaBuilder->buildSauce(); pizzaBuilder->buildTopping();
    }
    };
    // Клиент заказывает две пиццы. int main()
    {
    Waiter waiter;
    HawaiianPizzaBuilder hawaiianPizzaBuilder; waiter.SetPizzaBuilder (&hawaiianPizzaBuilder); waiter.ConstructPizza(); std::auto_ptr pizza = waiter.GetPizza(); pizza->ShowPizza();
    SpicyPizzaBuilder spicyPizzaBuilder; waiter.SetPizzaBuilder(&spicyPizzaBuilder); waiter.ConstructPizza(); pizza = waiter.GetPizza(); pizza->ShowPizza(); return EXIT_SUCCESS;
    }
    12.1.3 Фабричный метод (Factory Method)
    Фабричный метод (Factory Method)

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

    позволяет сделать код создания объектов более универсальным, не привязываясь к конкретным классам (
    ConcreteProduct
    ), а оперируя лишь общим интерфейсом (
    Product
    );

    позволяет установить связь между параллельными иерархиями клас- сов.
    Минусы:

    113
    Необходимо создавать наследника
    Creator для каждого нового типа продукта (
    ConcreteProduct
    ):

    продукт (
    Product
    ) определяет интерфейс объектов, создаваемых аб- страктным методом;

    конкретный продукт (
    ConcreteProduct
    ) реализует интерфейс
    Product
    ;

    создатель (
    Creator
    ) объявляет фабричный метод, который возвра- щает объект типа
    Product
    . Может также содержать реализацию этого ме- тода «по умолчанию»; может вызывать фабричный метод для создания объ- екта типа
    Product
    ;

    конкретный создатель (
    ConcreteCreator
    ) переопределяет фабрич- ный метод таким образом, чтобы он создавал и возвращал объект класса
    ConcreteProduct
    Применение:

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

    класс спроектирован так, чтобы объекты, которые он создаёт, специ- фицировались подклассами.

    класс делегирует свои обязанности одному из нескольких вспомога- тельных подклассов, и планируется локализовать знание о том, какой класс принимает эти обязанности на себя.
    #include
    #include using namespace std;
    // "Product" class Product{ public: virtual string getName() = 0;
    };
    // "ConcreteProductA" class ConcreteProductA : public Product { public: string getName() { return "ConcreteProductA";
    }
    };
    // "ConcreteProductB" class ConcreteProductB : public Product { public: string getName() { return "ConcreteProductB";
    }
    }; class Creator{ public: virtual Product* FactoryMethod() = 0;

    114
    };
    // "ConcreteCreatorA" class ConcreteCreatorA : public Creator { public:
    Product* FactoryMethod() { return new ConcreteProductA();
    }
    };
    // "ConcreteCreatorB" class ConcreteCreatorB : public Creator { public:
    Product* FactoryMethod() { return new ConcreteProductB();
    }
    }; int main() { const int size = 2;
    // An array of creators
    Creator* creators[size]; creators[0] = new ConcreteCreatorA(); creators[1] = new ConcreteCreatorB();
    // Iterate over creators and create products for(int i=0;i Product* product = creators[i]->FactoryMethod(); cout<
    getName()< } int a; cin >> a; for (int i=0;i } return 0;
    }
    1   ...   7   8   9   10   11   12   13   14   15


    написать администратору сайта