Форум: "Основная";
Текущий архив: 2009.04.19;
Скачать: [xml.tar.bz2];
ВнизFriend-классы Найти похожие ветки
← →
Vladix (2008-05-07 07:59) [0]Всем добрый день!
Подскажите, пожалуйста, есть ли в Delphi аналоги Си-шных friend-классов?
Напихивание кода в один модуль для того, чтобы была возможность видеть поля класса, меня не устраивает, так как при этом модули разрастаются, превращаясь в кашу. А делать поля класса общедоступными тоже не хочется.
Может, есть какие-нибудь директивы компилятора, или в новых версиях Delphi что-то появилось?
← →
Сергей М. © (2008-05-07 09:38) [1]
> или в новых версиях Delphi что-то появилось?
В новых - это в каких ? В Д7 их точно нет.
А что, без этой самой "дружественности" уж совсем никак не обойтись ? На ней прямо-таки свет клином сошелся ?
← →
Хитрий Лис (2008-05-07 09:59) [2]
> есть ли в Delphi аналоги Си-шных friend-классов
Прямых аналогов нет.
> так как при этом модули разрастаются, превращаясь в кашу
А может подойти к вопросу с другой стороны ?
1. Научиться пользоваться Class Explorer.
2. Увеличить mental power skill.
PS: D5 unit Excel2000 - 65957 строк/5 032 718 байт - каши нет :)
← →
Ega23 © (2008-05-07 10:13) [3]Есть интерфейсы.
← →
Ins © (2008-05-07 10:38) [4]
> Напихивание кода в один модуль для того, чтобы была возможность
> видеть поля класса, меня не устраивает, так как при этом
> модули разрастаются, превращаясь в кашу. А делать поля класса
> общедоступными тоже не хочется.
Законный способ - размещение в одном модуле. Что-то мне кажется, что такое количество друзей у класса, что в модуле получится каша - это признак плохой архитектуры
← →
Vladix (2008-05-07 10:50) [5]
> А что, без этой самой "дружественности" уж совсем никак
> не обойтись ? На ней прямо-таки свет клином сошелся ?
Обойтись, конечно, можно.. И свет на "дружественности" клином не сошелся.. Речь идет только о том, что есть моменты, когда "дружественность" действительно была бы удобна. Я не поклонник Си, но friend-классы считаю удобной вещью для разделения функционала по разным модулям.
К примеру, есть класс, отвечающий за выполнение некоторых функций. Рядом есть иерархия классов, отвечающих за отображение данных первого класса на различные визуальные контролы. И плюс - имеется иерархия классов для сохранения данных первого класса в различные хранилища.
Так вот, все классы этих двух иерархий обязаны знать о том, как устроен первый класс.
Все сваливать в один модуль не очень хочется, а по-другому как?
> Есть интерфейсы.
Замечательно, что они есть )) и пользоваться ими я умею. Но в данном случае это мало чем помогает: все методы интерфейсов - открытые, какой тут может быть разговор об инкапсуляции?
← →
Ins © (2008-05-07 10:55) [6]
> Но в данном случае это мало чем помогает: все методы интерфейсов
> - открытые, какой тут может быть разговор об инкапсуляции?
>
С интерфейсами просто можно поступить хитро. Закрыть нужные методы класса в приват и сделать их реализациией интерфейса. Тогда клиентский код сможет получить доступ к этим методам только запросив интерфейс. Запрос интерфейса - это будет своего рода "код доступа" и подтверждение, что клиент знает, что делает. Ну а еще уже в самом коде запроса интерфейса можно сделать некую хитрую проверку на то, имеет ли данный код право на доступ к методам.
← →
Юрий Зотов © (2008-05-07 11:00) [7]> Vladix (07.05.08 07:59)
> Подскажите, пожалуйста, есть ли в Delphi аналоги Си-шных
> friend-классов?
В некотором роде есть.
1. Загоняем нужные поля класса TMyClass в его секцию protected.
2. В другом модуле объявляем:
type
TMyFriendClass = class(TMyClass);
3. Ниже этого объявления секция protected будет доступна после приведения типа объекта к TMyFriendClass:
TMyFriendClass(ObjectOfTMyClass).ProtectedField := ...;
← →
Ins © (2008-05-07 11:11) [8]
> Юрий Зотов © (07.05.08 11:00) [7]
Полагаю, это не совсем то. В данном случае, доступ к закрытим членам получит каждый желающий, а смысл дружественных классов - это разграничение доступа - кому-то доступ разрешен, а кому-то запрещен. Я предлагаю такой вариант:
1. Реализуем в защищаемом классе IInterface и свой интерфейс, содержащий защищаемые методы.
2. Помещаем защищаемые методы в секцию private
3. Реализуем в классе приватное поле (или свойство) AllowAccess: Boolean;
4. Реализуем в классе _QueryInterface таким образом, чтобы он проверял AllowAccess и в случае False не давал доступ к определенному в п.1. интерфейсу.
5. В том же юните реализуем класс-стражник, к которому клиент будет обращаться, когда ему нужен доступ к интерфейсу.
5. У этого класса - один метод: AccessCheck(Client: TObject; Server: TProtectedObject): ISomeInterface. Код этого метода проверяет, имеет ли право Client на доступ (например, ищет класс клиента в неком реестре) и если имеет - выставляет серверу AllowAccess в True, вызывает у него QueryInterface, сбрасывает AllowAccess в False. Если не разрешен - возвращает nil.
← →
Ins © (2008-05-07 11:24) [9]Хотя нет, так не получится. Клиент, когда запросит интерфейс у объектной ссылки, а не у интерфейсной, это произойдет напрямую через GetInterface, минуя QueryInterface. Так что либо скармливать клиенту интерфейсную ссылку вместо объектной, либо я еще покурю, и что-либо придумаю :)
← →
Игорь Шевченко © (2008-05-07 11:38) [10]
> Подскажите, пожалуйста, есть ли в Delphi аналоги Си-шных
> friend-классов?
friend-ы в С++ сделаны от безысходности. Аналогов в Delphi нет, потому что они не нужны.
← →
Ins © (2008-05-07 11:46) [11]В общем, получилось вот что:
type
TProtectedObject = class;
// Интерфейс доступа к защищенному полю X
IMyInterface = interface
["{9963190E-2137-4247-8CDA-EBFAB63D960A}"]
function GetX: Integer;
end;
// Класс, реализующий интерфейс IMyInterface у защищаемого класса TProtectedObject
TIntfImpl = class(TAggregatedObject, IMyInterface)
private
FController: TProtectedObject;
function GetX: Integer;
public
constructor Create(const Controller: TProtectedObject);
end;
// Защищаемый класс. Поле X доступно лишь избранным :)
TProtectedObject = class(TObject, IInterface, IMyInterface)
private
X: Integer;
FAllowAccess: Boolean;
FIntfImpl: TIntfImpl;
function GetIntfImpl: IMyInterface;
// IInterface
function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
function _AddRef: Integer; stdcall;
function _Release: Integer; stdcall;
// IMyInterface
// Теперь клиент, запросив интерфейс IMyInterface, не пройдет мимо нашего
// геттера :)
property IntfImpl: IMyInterface read GetIntfImpl implements IMyInterface;
public
constructor Create;
destructor Destroy; override;
end;
// Класс, у которого мы будем просить доступ
TSecurityClass = class
public
class function QueryAccess(Client: TObject; Server: TProtectedObject): IMyInterface;
end;
implementation
{ TProtectedObject }
function TProtectedObject._AddRef: Integer;
begin
Result := -1;
end;
function TProtectedObject._Release: Integer;
begin
Result := -1;
end;
function TProtectedObject.QueryInterface(const IID: TGUID;
out Obj): HResult;
begin
if GetInterface(IID, Obj) then
Result := 0
else
Result := E_NOINTERFACE;
end;
function TProtectedObject.GetIntfImpl: IMyInterface;
begin
if FAllowAccess then
Result := FIntfImpl
else
Result := nil;
end;
constructor TProtectedObject.Create;
begin
inherited Create;
FIntfImpl := TIntfImpl.Create(Self);
end;
destructor TProtectedObject.Destroy;
begin
FIntfImpl.Free;
inherited Destroy;
end;
{ TIntfImpl }
constructor TIntfImpl.Create(const Controller: TProtectedObject);
begin
inherited Create(Controller as IInterface);
FController := Controller;
end;
function TIntfImpl.GetX: Integer;
begin
Result := FController.X;
end;
{ TSecurityClass }
class function TSecurityClass.QueryAccess(Client: TObject;
Server: TProtectedObject): IMyInterface;
begin
Result := nil;
if <класс клиента является дружественным> then begin
Server.FAllowAccess := True;
try
Result := Server as IMyInterface;
finally
Server.FAllowAccess := False;
end;
end;
end;
Теперь получить доступ к полю X можно только запросом у TSecurityClass. А там - мы полностью контролируем, кому давать доступ, а кому - не давать. Список дружественных классов можно реализовать на основе TClassList.
← →
Ins © (2008-05-07 11:56) [12]
function TProtectedObject.GetIntfImpl: IMyInterface;
begin
if FAllowAccess then
Result := FIntfImpl
else
Result := nil; // Тут на самом деле лучше кинуть исключение InvalidTypecast
end;
← →
Ins © (2008-05-07 12:24) [13]
> friend-ы в С++ сделаны от безысходности. Аналогов в Delphi
> нет, потому что они не нужны.
Не думаю, что от безысходности, они таким образом просто хотели задать более гибкий механизм управления видимостью, чем это делается обычным образом с помощью директив private/protected/public. Гибкий то он действительно более гибкий, но вот то, что сомнительный - это точно.
← →
Vladix (2008-05-07 12:26) [14]
> Ins ©
Код совсем получился неудобочитаемым.. но самое главное - идея, и она понятна. Спасибо за предложенный вариант!
> Игорь Шевченко © (07.05.08 11:38) [10]
> friend-ы в С++ сделаны от безысходности. Аналогов в Delphi
> нет, потому что они не нужны.
Ответ, достойный магистра.. Если денег нет, значит они не нужны )))
Что нет в C++ сравнительно с Delphi, чтобы Страуструпу от безысходности пришлось в спешном порядке придумывать "дружественность"? Доступа "всему ко всему" в рамках одного модуля? Если так, то "дружественность" выглядит симпатичнее и по крайней мере объявляется в тексте модулей.
← →
Игорь Шевченко © (2008-05-07 12:47) [15]Vladix (07.05.08 12:26) [14]
> Что нет в C++ сравнительно с Delphi
Модульности
← →
Юрий Зотов © (2008-05-07 16:50) [16]> Ins © (07.05.08 11:11) [8]
> Полагаю, это не совсем то.
Что и было сказано - "в некотором роде".
Страницы: 1 вся ветка
Форум: "Основная";
Текущий архив: 2009.04.19;
Скачать: [xml.tar.bz2];
Память: 0.51 MB
Время: 0.046 c