|
Программирование >> Арифметические и логические операции
return malloc; else return 0; Это - все, что надо знать, чтобы начать использовать оболочку Windows и ее COM интерфейсы. Ниже приводится пример. Оболочка Windows имеет понятие Рабочего стола, являющегося корнем файловой системы. Вы обращали внимание, как Windows приложения допускают пользователя, просматривают файловую систему, начинающуюся на рабочем столе? Этим способом вы можете, например, создавать файлы непосредственно на вашем рабочем столе, двигаться между дисководами, просматривать сетевой дисковод, и т.д. Это, в действительности, Распределенная Файловая система (PMDFS - poor mans Distributed File System). Как ваше приложение может получить доступ к PMDFS? Просто. В качестве примера напишем код, который позволит пользователю выбирать папку, просматривая PMDFS. Все, что мы должны сделать - это овладеть рабочим столом, позиционироваться относительно его, запустить встроенное окно просмотра и сформировать путь, который выбрал пользователь. char path [MAX PATH]; path [0] = \0; Desktop desktop; ShPath browseRoot (desktop, unicodePath); if (browseRoot.IsOK ()) FolderBrowser browser (hwnd, browseRoot, BIF RETURNONLYFSDIRS, Select folder of your choice ); if (folder.IsOK ()) strcpy (path, browser.GetPath ()); Давайте, запустим объект desktop. Он использует интерфейс по имени IShellFolder. Обратите внимание, как мы приходим к Первому Правилу Захвата. Мы распределяем ресурсы в конструкторе, вызывая функцию API SHGetDesktopFolder. Интеллектуальный указатель интерфейса будет заботиться об управлении ресурсами (подсчет ссылок). class Desktop: public SIfacePtr<IShellFolder> public: Desktop () if (SHGetDesktopFolder (& p) != NOERROR) throw SHGetDesktopFolder failed ; Как только мы получили рабочий стол, мы должны создать специальный вид пути, который используется PMDFS. Класс ShPath инкапсулирует этот путь . Он создан из правильного Unicode пути (используйте mbstowcs, чтобы преобразовать путь ASCII в Unicode: int mbstowcs(wchar t *wchar, const char *mbchar, size t count)). Результат преобразования - обобщенный путь относительно рабочего стола. Обратите внимание, что память для нового пути распределена оболочкой - мы инкапсулируем это в SShellPtr, чтобы быть уверенными в правильном освобождении. class ShPath: public SShellPtr<ITEMIDLIST> public: ShPath (SIfacePtr<IShellFolder> & folder, wchar t * path) ULONG lenParsed = 0; hresult = folder->ParseDisplayName (0, 0, path, & lenParsed, & p, 0); bool IsOK () const { return SUCCEEDED ( hresult); } private: HRESULT hresult; Этот путь оболочки станет корнем, из которого окно просмотра начнет его взаимодействие с пользователем. С точки зрения клиентского кода, окно просмотра - путь, выбранный пользователем. Именно поэтому он наследуется от SShellPtr<ITEMIDLIST>. Между прочим, ITEMIDLIST - официальное имя для этого обобщенного пути. class FolderBrowser: public SShellPtr<ITEMIDLIST> public: FolderBrowser ( HWND hwndOwner, SShellPtr<ITEMIDLIST> & root, UINT browseForWhat, char const *title); char const * GetDisplayName () { return displayName; } char const * GetPath () { return fullPath; } bool IsOK() const { return p != 0; }; private: char displayName [MAX PATH]; char fullPath [MAX PATH]; BROWSEINFO browseInfo; FolderBrowser::FolderBrowser ( HWND hwndOwner, SShellPtr<ITEMIDLIST> & root, UINT browseForWhat, char const *title) displayName [0] = \0; fullPath [0] = \0; browseInfo.hwndOwner = hwndOwner; browseInfo.pidlRoot = root; browseInfo.pszDisplayName = displayName; browseInfo.lpszTitle = title; browseInfo.ulFlags = browseForWhat; browseInfo.lpfn = 0; browseInfo.lParam = 0; browseInfo.iImage = 0; Let the user do the browsing p = SHBrowseForFolder (& browseInfo); if ( p != 0) SHGetPathFromIDList ( p, fullPath); Вот так! Разве это не просто? Как функции, не являющиеся методами, улучшают инкапсуляцию Когда приходится инкапсулировать, то иногда лучше меньше, чем больше. Если вы пишете функцию, которая может быть выполнена или как метод класса, или быть внешней по отношению к классу, вы должны предпочесть ее реализацию без использования метода. Такое решение увеличивает инкапсуляцию класса. Когда вы думаете об использовании инкапсуляции, вы должны думать том, чтобы не использовать методы. При изучении проблемы определения функций, связанных с классом для заданного класса C и функции f, связанной с C, рассмотрим следующий алгоритм: if (f необходимо быть виртуальной) сделайте f функцией-членом C; else if (f - это operator или operator ) сделайте f функцией - не членом; if (f необходим доступ к непубличным членам C) сделайте f другом C; else if (в f надо преобразовывать тип его крайнего левого аргумента) сделайте f функцией - не членом; if (f необходимо иметь доступ к непубличным членам C) сделайте f другом C; else сделайте f функцией-членом C; Этот алгоритм показывает, что функции должны быть методами даже тогда, когда они могли бы быть реализованы как не члены, которые использовали только открытый интерфейс класса C. Другими словами, если f могла бы быть реализована как функция-член (метод) или как функция не являющаяся не другом, не членом, действительно ее надо реализовать как метод класса? Это не то, то, что подразумевалось. Поэтому алгоритм был изменен: if (f необходимо быть виртуальной) сделайте f функцией-членом C; else if (f - это operator>> или operator<<) сделайте f функцией - не членом; if (f необходим доступ к непубличным членам C) сделайте f другом C; else if (f необходимо преобразовывать тип его крайнего левого аргумента) сделайте f функцией - не членом; if (f необходимо иметь доступ к непубличным членам C) сделайте f другом C; else if (f может быть реализована через доступный интерфейс класса) сделайте f функцией - не членом; else сделайте f функцией-членом C; Инкапсуляция не определяет вершину мира. Нет ничего такого, что могло бы возвысить инкапсуляцию. Она полезна только потому, что влияет на другие аспекты нашей программы, о которых мы заботимся. В частности, она обеспечивает гибкость программы и ее устойчивость к ошибкам. Посмотрите на эту структуру, чья реализация не является инкапсулированной: struct Point { int x, y; Слабостью этой структуры является то, что она не обладает гибкостью при ее изменении. Как только клиенты начнут использовать эту структуру, будет очень тяжело изменить ее. Придется изменять слишком много клиентского кода. Если бы мы позднее решили, что хотели бы вычислять x и y вместо того, чтобы хранить эти значения, мы были бы обречены на неудачу. У нас возникли бы аналогичные проблемы при запоздалом озарении, что программа должна хранить x и y в базе данных. Это реальная проблема при недостаточной инкапсуляции: имеется препятствие для будущих изменений реализации. Неинкапсулированное программное обеспечение негибко, и, в результате, оно не очень устойчиво. При изменении внешних условий программное обеспечение неспособно элегантно измениться вместе с ними. Не забывайте, что мы говорим здесь о практической стороне, а не о том, что является потенциально возможным. Понятно, что можно изменить структуру Point. Но, если большой объем кода зависит от этой структуры, то такие изменения не являются практичными. Перейдем к рассмотрению класса с интерфейсом, предлагающим клиентам возможности, подобные тем, которые предоставляет выше описанная структура, но с инкапсулированной реализацией: class Point { public: int getXValue() const; int getYValue() const; void setXValue(int newXValue); void setYValue(int newYValue); private: прочее... Этот интерфейс поддерживает реализацию, используемую структурой (сохраняющей x и y как целые), но он также предоставляет альтернативные реализации, основанные, например, на вычислении или просмотре базы данных. Это более гибкий замысел, и гибкость делает возникающее в результате программное обеспечение более устойчивым. Если реализация класса найдена недостаточной, она может быть изменена без изменения клиентского кода. Принятые объявления доступных методов остаются, неизменными, что ведет к неизменности клиентского исходного текста. Инкапсулированное программное обеспечение более гибко, чем неинкапсулированное, и, при прочих равных условиях, эта гибкость делает его предпочтительнее при выборе метода проектирования. Степень инкапсуляции Класс, рассмотренный выше, не полностью инкапсулирует свою реализацию. Если реализация изменяется, то еще имеется код, который может быть изменен. В частности, методы класса могут оказаться нарушенными. По всей видимости, они зависят от особенностей данных класса. Однако ясно видно, что класс более инкапсулирован, чем структура, и хотелось бы иметь способ установить это более формально. Это легко сделать. Причина, по которой класс является более инкапсулированным, чем структура, заключается в том, что при изменении открытых данных структуры может оказаться разрушенным больше кода, чем при изменением закрытых данных класса. Это ведет к следующему подходу в оценке двух реализаций инкапсуляции: если изменение для одной реализации может привести к большему разрушению кода, чем это разрушение будет при другой реализации, то соответствующее изменение для первой реализации, будет менее инкапсулировано. Это определение совместимо с нашей интуицией, которая подсказывает нам, что вносить изменения следует таким образом, чтобы разрушать как можно меньше кода. Имеется прямая связь между инкапсуляцией (сколько кода могут разрушить вносимые изменения) и практической гибкостью (вероятность, что мы будем делать специфические изменения). Простой способ измерить, сколько кода может быть разрушено, состоит в том, чтобы считать функции, на которые пришлось бы воздействовать. То есть, если изменение одной реализации ведет потенциально к большему числу разрушаемых функций, чем изменения в другой реа-
|
© 2006 - 2025 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки. |