Возможно, описанная мной проблема тянет на маленькую статью, поэтому тов. модераторы, можете перенести ее туда, куда Вам покажется более верным.
Строго говоря, этот пост не является каким-то конкретным вопросом. Просто я излагаю свою небольшую идею и хочу знать, как эту идею примет уважаемое сообщество RSDN. Единственная просьба — не бейте камнями, а точнее вопросами — "Да зачем все это надо ??? У меня вот нет таких проблем... потому что я пишу вот так мол и так...", поймите, я не могу отмахнуться от задачи, просто отказавшись ее решать.
— <<Введение>>
Меня зовут Борис, я работаю в Программе Микрофинансирования Европейского Банка Реконструкции и Развития. Одна из задач, которые передо мной стоят — задача отслеживания заявок, кредитов, заемщиков и т.д. в банках-партнерах, с которыми работает наша Программа. Не вдаваясь а дальнейшие детали, перейду непосредственно к вопросу.
+ <<Задача: модель данных>>
В настоящий момент я разрабатываю модель данных для довольно сложной комплексной проблемы учета. Модель включает в себя массу информации из реального мира, которую не так то просто промоделировать. Конечным результатом должен стать модуль, написанный на языке Python, т.е. грубо говоря, некая иерархия классов, отражающая предметную область.
Я буду приводить примеры на С++ чтобы было более понятно аудитории, потому как по данным опроса большинство программистов RSDN используют C++. Пожалуйста, отнеситесь со снисхождением к коду, который я буду писать на С++, потому что у меня нет возможности (да и желания) его хотя-бы раз скомпилировать, а потому возможны разные синтаксические ошибки.
+ <<Основной затык>>
Разрабатывая проблему, я столкнулся со следующим весьма печальным (и, в общем-то, известным) затыком: статическая объектная модель данных (т.е. иерархия классов) не может достаточно полно и точно описать предметную область.
Сразу оговорюсь, что при разработке модели Я НЕ ПЫТАЛСЯ объять необъятное, т.е. разработать модель для всех возможных случаев. Это попросту невозможно.
— <<Пример>>
В предметной области имеются люди как таковые, то есть некоторые объекты, имеющие фамилию имя и отчество (строго говоря, для какой-то другой предметной области важнее будет, что люди имеют, скажем, рост и вес, но в данном случае я разрабатываю совершенно конкретную областьи потому важно именно ФИО), на C++ это может быть описано примерно так:
class Person {
public string first_name;
public string last_name;
public string third_name;
}
Люди могут при этом быть пользователями системы:
class User: public Person {
public string login;
public string password;
}
Но кроме этого, люди могут быть еще и подателями заявлений
class Applicant: public Person {
public Application application;
public date applicationDate;
}
Может быть такая ситуация, что человек является подателем заявления (Applicant) но не является пользователем системы (User). А может быть и наоборот — человек может являться пользователем системы и не являться подателем заявления.
— <<Почему так происходит>>
Проблема в том, что статическое описание классов (диаграмма классов) не может описать динамический характер предметной области.
— <<Обычное решение>>
Обычное решение (к сожалению) состоит в том, чтобы отказаться от классического ОО моделирования предметной области, исскуственно упрощая модель данных, например:
class User {
public string first_name;
public string second_name;
public string third_name;
public string login;
public string password;
}
class Appplicant {
public string first_name;
public string second_name;
public string third_name;
public Application app;
}
Преимущества и недостатки такого подхода говорят сами за себя:
\- Это проще реализовать
\- К сожалению, теперь уже ПРИЛОЖЕНИЕ должно знать о связях между объектами, т.е. приложение должно знать, скажем, что пользователь и заявитель с одинаковыми ФИО — это одно и то же лицо.
— <<Возможное статическое решение>>
Ранее я уже сталкивался с подобной проблемой в более мелких масштабах и решал я ее так:
class Person {
...
}
class User {
public Person person;
}
class Applicant {
public Applicant applicant;
}
Далее все понятно. Но есть несколько затыков:
1) Можно определить, что пользователь является человеком, но не наоборот (нельзя определить что данный конкретный человек является пользователем)
2) Четкую иерархию классов мы подменили исскуственной иерархией агрегации. Это, в общем, терпимо, однако заставляет нас писать дополнительный проблемно-ориентированный код, который реализует иерархию. Это "грязный хак".
До сих пор я считал, что это вообще единственное разумное решение.
+ <<Шаблонный подход, приближающий нас к идеалу>>
— <<Множественное наследование>>
Выразить факт того, что человек является пользователем или заявителем можно, используя множественное наследование
class PersonMixin {
public string first_name;
public string second_name;
public string third_name;
}
class UserMixin {
public string login;
public string password;
}
class Applicant {
public Application application;
}
теперь конкретный объект можно создать так:
class ConcreteUser: public PersonMixin, public UserMixin {
}
ConcreteUser user = ConcreteUser()
Множественное наследование не такая уж страшная вещь, как ее малюют. До тех пор, конечно, пока Вы не заморачиваетесь с виртуальными классами, т.е. пока классы, от которых Вы наследуете, не слишком сложны.
— <<Шаблоны>>
Шаблоны позволяют упростить использование множественного наследования
template <typename T, typename B> class Dual: public <T>, public <B> {
}
user = Dual<PersonMixin, UserMixin>()
примерно так или что-то в этом духе.
— <<Затык>>
К сожалению, и этот подход ведет в тупик, потому что невозможно изменить класс объекта в рантайме, т.е. если, например, был у нас Петр Петрович Иванов, и стал он вдруг пользователем системы, это мы уже не сможем выразить в коде. Вот если ЗАРАНЕЕ известно, что Петр Петрович является пользователем системы — тогда все Ок.
— <<Динамическое решение>>
К большому моему счастью (и, конечно, к несчастью, потому что в мире вообще сплошные палки с двумя концами) я пишу задачу не на С++, а на Питоне.
Питон является динамическим языком с очень мощными возможностями интроспекции. В частности, он позволяет изменять класс переменной ВО ВРЕМЯ ИСПОЛНЕНИЯ ПРОГРАММЫ!
Идея, кажется, не новая, потому что смутно помню, что я это уже где-то встречал. Но простота, с какой ее можно реализовать на Питоне, меня поразила...
Поэтому. Можно писать код в таком виде.
obj = object() # какой-то абстрактный объект
promote(obj, User) # объект стал пользователем
obj.login="login"
obj.password="password"
promote(obj, Person) # объект кроме того, что был пользователем, стал еще и человеком
obj.firstName = "Пасько"
obj.firstName = "Борис"
obj.firstName = "Сергеевич"
promote(obj, Person) # объект кроме того, что был пользователем и человеком, стал еще и заявителем
obj.application = Application(...)
Определения классов притимивны, как и в шаблонном примере:
class User:
login = None
password = None
class Person:
firstName = None
lastName = None
thirdName = None
class Applicant:
application = None
Реализация функции promote очень проста, но для ее понимания Вам понадобится разобраться в том, как организованы классы в Питоне. Можете этот пункт просто пропустить
def promote(obj, klass):
bases = obj.__class__.__bases__
bases = list(bases) # потому что obj.__class__.__bases__ - это не список, а tuple
bases.append(klass) # !!!
obj.__class__.__bases__=tuple(bases)
— <<Что это дает???>>
А дает такой подход несколько приятных возможностей.
1) Мы не ломаем ООП подход к нашей модели, просто рассматриваем возможность того, что классы могут изменяться в рантайме, т.е. расширяем ОО моделирование и на рантайм.
2) Мы можем использовать базовые возможности языка для работы с такими объектами. Например, при агрегационно подходе для того, чтобы изменить фамилию пользователя мы написали бы что-то вроде:
user.person.firstName = "Иванов"
в данном случае мы просто пишем
user.firstName = "Иванов"
Мы можем использовать явные проверки для определения класса объекта:
Здравствуйте, flax, Вы писали:
F>1) Распознавание конвертируемости и наследования на этапе компиляции. F>2) Оболочки вокруг TypeInfo для рантайма
К сожалению я не знаком ни с первым пунктом, ни со вторым. Я почитаю и отвечу.
Однако, дело ведь тут не в распознавании типа и конвертируемости, а в том, чтобы в рантайме ИЗМЕНИТЬ класс объекта, скажем, добавить еще один класс, к которому данный объект принадлежит/от которого он наследован.
Говоря очень утрированно, сегодня я, скажем, User, который работает в Ворде, а завтра подучился — глядишь и не только User, а еще и Programmer, который пишет под ворд макросы. А послезавтра выясняется, что я еще и Taxpayer.... и т.д.
Hello, borisman2!
You wrote on Fri, 01 Oct 2004 09:24:03 GMT:
Тоесть обьект обладает каким-то динамическим набором "возможностей" ?
Если да, то:
1. какждую "возможнось" сделать как СОМ-интерфейс обьекта ...
2. сделать класс, реализующий все эти интерфейсы ...
3. сделать, так чтобы ф-ция QueryInterface знала какой набор интерфейсов доступен в данный момент экемпляру обьекта, и возвращала только доступные ...
4. перед работой пробуем получить нужный интерфейс, если получилось работаем, если нет, то обьект это не может ..
Думаю что при таком подходе сохранятся все преемущества, описаные на примере реализации на Питоне
Здравствуйте, borisman2, Вы писали:
B>Люди могут при этом быть пользователями системы: B>Но кроме этого, люди могут быть еще и подателями заявлений
Если нужно все сделать статически, то надо делать так:
TYPE
Subject = OBJECT
Person : Person; (* Если Person = NIL, значит он не персона, system trap *)
User : User; (* Если User = NIL, значит он не юзер *)
Applicant : Applicant; (* Если Applicant = NIL, значит он не аппликант *)END;
Subjectable = ABSTRACT OBJECT
Subject : Subject;
END;
Person = OBJECT (Subjectable)
FirstName : STRING;
LastName : STRING;
ThirdName : STRING;
END;
User = OBJECT (Subjectable)
Login : STRING;
Password : STRING;
END;
Applicant = OBJECT (Subjectable)
App : Application;
AppData : ApplicationData;
END;
VAR X: Subject;
(* Должны выполняться тождества: *)
Если X.Person # NIL, то X = X.Person.Subject
Если X.User # NIL, то X = X.User.Subject
Если X.Applicant # NIL, то X = X.Applicant.Subject
B> — <<Почему так происходит>> B>Проблема в том, что статическое описание классов (диаграмма классов) не может описать динамический характер предметной области.
Почему бы не вынести такие свойства объекта в отдельные классы. А сам объект будет содержать набор этого хлама.
Например:
class BaseProp {
};
class LoginProp: public BaseProp {
string login;
string pwd;
};
class UserProp: public BaseProp {
string name;
int age;
};
class Object {
std::vector<BaseProp*> props;
};
Выглядит сыро, особенно для С++ Идею можно немного додумать и научиться получать нужные атрибуты и читать их. Например, по некому идентификатору или как-нить еще, это по личным предпочтениям. Структура будет динамичной, добавление новых свойств не составит труда.
Здравствуйте, borisman2, Вы писали:
B>+ Письмо в RSDN
1. Нисколько не умаляя достоиств питона (я и сам на нем постоянно пишу), хочу всё-таки заметить, что здесь существует вполне приемлемое статическое решение:
public interface IPerson
{
string FirstName { get; }
string LastName { get; }
}
public interface IUser: IPerson
{
string Login { get; }
string Password { get; }
}
...
// Соответственно, использовать это можно реализовав у твоих объектов функцию явного запроса интерфейса:
IUser asUser = person.QueryInterface( IUser);
...
2. Что же касается питона, то и там в большистве приведенных примеров также можно было обойтись без явной смены класса объекта.
Это же динамический язык. Для радостей навроде такого: B>в данном случае мы просто пишем B>
B>user.firstName = "Иванов"
B>
Достаточно просто грамотно перекрыть метод __getattr__.
Единсвенно, что эта проверка не будет проходить: B>
Но оно и не нужно, если вдуматься. Вот такое решение выглядит ничуть не сложнее:
obj.is_a( User)
obj.is_a( Applicant)
...
3. Ну, и последнее. Функция promote:
def promote(obj, klass):
bases = obj.__class__.__bases__
bases = list(bases) # потому что obj.__class__.__bases__ - это не список, а tuple
bases.append(klass) # !!!
obj.__class__.__bases__=tuple(bases)
Ты в курсе, что этот код изменяет базовый тип не только конкретного объекта, а и всех его братьев и сестер?
Если уж писать, то как-то так:
Здравствуйте, borisman2, Вы писали:
B>+ Письмо в RSDN
Отличная тема! Давненько такой не было. У меня периодически возникают какие-то мысли в эту сторону, но т.к. предметной области с такими запросами нет, я никак не могу проверить свои идеи.
По поводу множественной функциональности вроде как сейчас применяют интерфейсы. (Как тут уже было замечено). При моделировании все вроде бы должно сходиться идеально: мы описываем взаимоотношения объектов исключительно в терминах абстрактных интерфейсов, что потенциально позволяет нам состряпать класс, реализующий произвольную функциональность.
Есть две проблемы.
Перавя проблема — в комбинаторном взрыве количества классов. Пока мы рассуждаем в терминах IUser, IPerson — все очень здорово. У каждого IDocument есть IUser IDocument::Creator и IPerson Author. Все красиво, здорово, можно строить различные алгоритмы, требующие минимум от остальных компонентов системы.
Но когда мы начинаем собственно строить эти "остальные компоненты" у нас появляются и User, и Person, и PersonUser, который может быть сразу и автором и создателем. При этом вроде никакого особенного функционала в нашем PersonUser-то и нету. Так, банальная суперпозиция User и Person. Дальше станет еще хуже, когда список возможных ролей начнет расти.
Вторая проблема — в том, что один и тот же объект может играть в разное время разные роли. Раньше я думал, что это можно обойти при помощи замены объекта. То есть у нас есть либо PersonUser& Person::BecomeUser(), либо наоборот PersonUser::PersonUser(User me). Таким образом мы берем и создаем более сложный объект по более простому. Предыдущий объект умирает, и заменяется новым.
Vasya= Vasya.LearnToFly();
Увы, эта щтука совершенно не будет работать. Дело в том, что где-то далеко у нас может храниться ссылка на конкретно этот объект. Когда-то он был чем-то одним. Теперь, спустя много времни, мы сдуваем с этой ссылки пыль и проверям, а не реализует ли ссылаемый объект новый интерфейс? Увы, у нового объекта новая Identity. Либо ссылка прокисла, либо она показывает на васино привидение, либо мы мучительно обновляем все ссылки. Ни один из способов мне не нравится.
Мы должны сделать так, чтобы именно тот же самый объект сменил свой класс.
Во многих случаях обе проблемы скорее всего разрешимы именно агрегацией. У меня, как я уже жаловался, не хватает предметной области для упражнений в моделировании. Но то, что само приходит в голову, как правило решается легко и непринужденно. IUser превращается в IAccount, а персона получает возможность владеть несколькими аккаунтами. Адрес больше не является частью интерфейса IPerson (хотя так хочется его туда положить), а становится его агрегатом. Взамен персона получает адрес регистрации вместе с адресом постоянного проживания. При этом любой из них может оказаться адресом доставки для класса PersonalStoreRecord, реализующего IShoppingCartCustomer.
В таком случае вся динамика уходит в компоненты. А типизация остается статичной.
И вот я до сих пор не знаю — бывают ли ситуации, когда такой подход не сработает? По идее, это должна быть ситуация, в которой именно один и тот же объект можно равноправно трактовать как что-то одно, так и что-то другое. И более того, этот объект сначала этим чем-то другим не является, а потом — становится.
О! Вот задачка: был я простым персоном, а потом женился. Т.е. начал реализовывать
IMarried
{
public IPerson Spouse
{ get;}
}
Как бы обработать эту ситуацию?.. Будем думать.
... << RSDN@Home 1.1.4 beta 3 rev. 185>>
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, flax, Вы писали:
F>Здравствуйте, borisman2, Вы писали:
BB>>чтобы в рантайме ИЗМЕНИТЬ класс объекта
F>Тогда прямиком в рубрику dotnet. Там любят reflection по метаданным
О! В принципе вариант... Не знал я, что дотнет такое может...
Смотрю я на это, и вижу сплошные интерфейсы. Т.е. класс, выдающий (реализующий) несколько интерфейсов по запросу. Надо юзера — запросил интерфейс юзера, ещё кого — тоже просим. Проверить, поддерживает ли нужный нам интерфейс — тоже можем.
Нет, я конечно понимаю , что превращать один класс в другой или там даже не превращать, а расширять до другого, ведь это я так понял позволяет сделать Питон, интересно.
B>Говоря очень утрированно, сегодня я, скажем, User, который работает в Ворде, а завтра подучился — глядишь и не только User, а еще и Programmer, который пишет под ворд макросы. А послезавтра выясняется, что я еще и Taxpayer.... и т.д.
Ну а с другой стороны, это всё тот же объект юзер в одном числе, только сначала он был в коллекции Вордораб, потом добавили ссылку ещё и в Программёр, а послезавтра включили ещё в одну коллекцию. Те же Элементы и Коллекции, Пользователи и Группы... Разве нет?
... << RSDN@Home 1.1.4 beta 3 rev. 189 Тишь да гладь, да Божья благодать >>
Здравствуйте, akasoft, Вы писали:
A>Здравствуйте, borisman2, Вы писали:
A>Смотрю я на это, и вижу сплошные интерфейсы. Т.е. класс, выдающий (реализующий) несколько интерфейсов по запросу. Надо юзера — запросил интерфейс юзера, ещё кого — тоже просим. Проверить, поддерживает ли нужный нам интерфейс — тоже можем.
Вкратце. Интерфейсы тут вообще не при чем.
Подробно:
Строго говоря, я даже слово "интерфейс" не разу не упоминал. Это потому, что я говорил не об интерфесах, а о попытке ЛУЧШЕ смоделировать реальный мир с его динамически меняющейся иерархией классов. Классическое объектно-ориентированное моделирование работает (очень грубо) по следующему алгоритму:
1) Определить все наличествующие в предметной области ОБЪЕКТЫ
2) Сгруппировать объекты с одинаковыми свойствами в КЛАССЫ
3) Построить из классов ИЕРАРХИЮ КЛАССОВ.
Здесь нет ни слова об интерфесах, а особенно, о запросах каких-то интерфесов. Я даже о методах-то не говорил. Вопрос интерфесов встает чуть позже, когда Вам нужно рассмотреть модель под каким-то усеченным углом, т.е. когда Вы хотели бы избавиться от ненужных деталей и оставить нужные — тогда вы начинаете работать над ВИДАМИ модели, которые, в частности, удобно представлять наборами и иерархиями интерфесов.
Я, в общем-то, не собраюсь рушить очень мне симпатичный метод объектного моделирования. Просто я говорю о том, что невозможно СТАТИЧЕСКИ смоделировать предметную обасть, т.е. отразить ее в СТАТИЧЕСКУЮ иерархию классов.
Цель разработанного мной способа не состоит в том, чтобы предоставить пользователю тот интерфейс, который ему нужен. Такая цель вовсе не ставилась. Задача была в том, чтобы пользователь мог определить, к какому КЛАССУ относится данный объект, и, при необходимости, классифицировать его по-новому, т.е. отнести его к новому классу предметов.
A>Нет, я конечно понимаю , что превращать один класс в другой или там даже не превращать, а расширять до другого, ведь это я так понял позволяет сделать Питон, интересно.
Да, интересно. Вопрос в том, имеет ли это практический смысл
Мне кажется, имеет. Однако могут быть и иные мнения.
A>Ну а с другой стороны, это всё тот же объект юзер в одном числе, только сначала он был в коллекции Вордораб, потом добавили ссылку ещё и в Программёр, а послезавтра включили ещё в одну коллекцию. Те же Элементы и Коллекции, Пользователи и Группы... Разве нет?
Да, в общем-то Вы верно оцениваете ситуацию. Тот же самый объект. Тот же самый юзер. Только, сегодня выяснилось, что он не только юзер, но еще и программер. В этом вся прелесть...
Насчет коллекций, мне кажется, или Вы немного недопоняли меня, или я — Вас. Если под коллекциями Вы имели в виду экстенты (extent), то есть списки всех объектов данного класса (как в ООБД), то, в общем-то, Вы частично правы. Однако это вовсе не те коллекции (типа TCollection в Delphi или Collection в Java), в которых обычно программисты складируют объекты. Такие коллекции опять же совершенно ни при чем.
Здравствуйте, Pretorean, Вы писали:
P>Hello, borisman2! P>You wrote on Fri, 01 Oct 2004 09:24:03 GMT:
P>Тоесть обьект обладает каким-то динамическим набором "возможностей" ? P>Если да, то:
P>1. какждую "возможнось" сделать как СОМ-интерфейс обьекта ... P>2. сделать класс, реализующий все эти интерфейсы ... P>3. сделать, так чтобы ф-ция QueryInterface знала какой набор интерфейсов доступен в данный момент экемпляру обьекта, и возвращала только доступные ... P>4. перед работой пробуем получить нужный интерфейс, если получилось работаем, если нет, то обьект это не может ..
P>Думаю что при таком подходе сохранятся все преемущества, описаные на примере реализации на Питоне
Да, кажется мы с Вами понимем друг друга. Осталось только добавить к Вашим рассуждениям метод, при помощи которого КЛИЕНТ сможет указать объекту, какого класса объектом он является (т.е. в Вашей терминилогии, какой он имеет интерфейс, хотя есть тонкая разница).
Да, можно попытаться сделать это на COM'е. Очень даже было бы интересно.
Здравствуйте, Сергей Губанов, Вы писали:
СГ>Здравствуйте, borisman2, Вы писали:
B>>Люди могут при этом быть пользователями системы: B>>Но кроме этого, люди могут быть еще и подателями заявлений
СГ>Если нужно все сделать статически, то надо делать так: СГ>[pascal] СГ>TYPE СГ> Subject = OBJECT СГ> Person : Person; (* Если Person = NIL, значит он не персона, system trap *) СГ> User : User; (* Если User = NIL, значит он не юзер *) СГ> Applicant : Applicant; (* Если Applicant = NIL, значит он не аппликант *) СГ> END;
Все верно, Вы сейчас идете по пути, который я описал как <<Возможное статическое решение>>
Проблема такого подхода заключается в том, что Вам придется заранее четко определить, к каким классом ПОТЕНЦИАЛЬНО может относиться объект. Иногда это очень даже разумно, например, когда известно точно, что люди могут быть слесарями, сантехникаим или уборщицами. Но это заранее накладывает ограничения на расширение модели в рантайме. Вы просто не сумеете предугадать, к каким классам ЕЩЕ можно отнести человека. Только, пожалуйста, не надо рубить с плеча — мол "это не нужно". В некоторых полуразумных приложениях может и понадобиться. Примером могут являться всем известные экспертные системы.
Впрочем, опять же, как я писал в описании <<Возможное статическое решение>>, это очень неплохой подход, до сих пор я вообще считал его единственно возможным
Здравствуйте, StanislavK, Вы писали:
B>> — <<Почему так происходит>> B>>Проблема в том, что статическое описание классов (диаграмма классов) не может описать динамический характер предметной области.
SK>Почему бы не вынести такие свойства объекта в отдельные классы. А сам объект будет содержать набор этого хлама. SK>Например:
Да, да, да! Именно так оно и будет выглядеть на С++.
SK>Выглядит сыро, особенно для С++ Идею можно немного додумать и научиться получать нужные атрибуты и читать их. Например, по некому идентификатору или как-нить еще, это по личным предпочтениям. Структура будет динамичной, добавление новых свойств не составит труда.
Вот именно, траблы-граблы только в том, что теперь придется построить свой небольшой язык (или назовите это набором функций), который сможет с такими объектами работать (не такая уж и сложная задача). Прелесть решения на Питоне в том, что нового языка делать не придется. Ну, что-ж поделать, за это тоже кой-чем приходится платить.
Кстати, Вы заметили, что наш с Вами подход к ОО моделированию сильно пошел в разрез с реализацией ООП как такового ? Это наша с Вами проблема или проблема ООП ? Я давно подозреваю, что ООП вообще вещь очень мутная — черти там водятся...
Прежде всего, огромное спасибо, господин товарищ eugals! Ваш ответ очень мне помог и я несомненно включу его как часть проектной документации, в которую я уже подшил свое письмо.
Вы писали:
E>1. Нисколько не умаляя достоиств питона (я и сам на нем постоянно пишу), хочу всё-таки заметить, что здесь существует вполне приемлемое статическое решение:
проблема состоит в том, как добавлять в существующие объекты новую функциональность.
E>2. Что же касается питона, то и там в большистве приведенных примеров также можно было обойтись без явной смены класса объекта. E>Это же динамический язык. Для радостей навроде такого: B>>в данном случае мы просто пишем B>>
B>>user.firstName = "Иванов"
B>>
E>Достаточно просто грамотно перекрыть метод __getattr__.
Абсолютно верно! Однако, ведь весь огород городится вовсе не из-за того, чтобы получить доступ к каким-то там новым атрибутам. Весь огород городился ради того, чтобы можно было объект рассматривать как объект ДЕЙСТВИТЕЛЬНО нового класса. Хотя, возможны, повторю, альтернативные решения. Кстати говоря, в первой реализации у меня так примерно и было сделано — я переопределял __getattr__ и все пучком. Но потом я задумался — а не дурак ли я? Получается, что я пытаюсь исскуственно приделать к объекту свойства другого класса. Зачем ? Почему бы объекту не стать обхектом нового класса ? Концептуально это проще и понятнее, чем рассматривать хитрые "прокси", которые, допустим, вызывают непонятно какие объекты на другой стороне.
Хотя, опять же, повторюсь, возможных решений масса, особенно на Питоне, который очень далеко позволяет влазить в кишки языка.
E>Единсвенно, что эта проверка не будет проходить: B>>
E>Но оно и не нужно, если вдуматься. Вот такое решение выглядит ничуть не сложнее: E>
E>obj.is_a( User)
E>obj.is_a( Applicant)
E>...
E>
Тоже верно! Но только зачем создавать дополнительные сущности без необходимости?
E>3. Ну, и последнее. Функция promote: E>Если уж писать, то как-то так: E>
Вот тут снимаю перед Вами шляпу, которую я прошляпил, когда писал эту функцию. Огромное спасибо. Это я действительно проглядел... Причем, заметьте, Ваше решение короче и понятней, чем мое. К тому же оно правильное, что немаловажно
Здравствуйте, eugals, Вы писали:
E>Здравствуйте, Sinclair, Вы писали:
S>>О! Вот задачка: был я простым персоном, а потом женился. Т.е. начал реализовывать S>>Как бы обработать эту ситуацию?..
E>Я бы сделал как-то так:
[поскипано]
Здравствуйте, borisman2, Вы писали:
B>Кстати, Вы заметили, что наш с Вами подход к ОО моделированию сильно пошел в разрез с реализацией ООП как такового ?
Какого ООП? Они бывают разные... Есть
1)Наследование
2)Инкапсуляция
3)Полиформизм
А есть ООП по Алану Кейю(на счет имени не уверен )
1)Объект — базовая единица системы.
2)Объекты обладают состоянием.
3)Единственным способом взаимодействия между объектами является посылка сообщения. Объекты сами определяют как обрабатывать сообщения. B>Это наша с Вами проблема или проблема ООП ? Я давно подозреваю, что ООП вообще вещь очень мутная — черти там водятся...
Дык вот ваш подход очень даже вписывается в ООП по Кейю
... << RSDN@Home 1.1.4 rev. 185 >>
Пусть это будет просто:
просто, как только можно,
но не проще.
(C) А. Эйнштейн