• Vladix (07.05.08 07:59) [0]
    Всем добрый день!

    Подскажите, пожалуйста, есть ли в Delphi аналоги Си-шных friend-классов?

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

    Может, есть какие-нибудь директивы компилятора, или в новых версиях Delphi что-то появилось?
  • Сергей М. © (07.05.08 09:38) [1]

    > или в новых версиях Delphi что-то появилось?


    В новых - это в каких ? В Д7 их точно нет.

    А что, без этой самой "дружественности" уж совсем никак не обойтись ? На ней прямо-таки свет клином сошелся ?
  • Хитрий Лис (07.05.08 09:59) [2]

    > есть ли в Delphi аналоги Си-шных friend-классов

    Прямых аналогов нет.


    > так как при этом модули разрастаются, превращаясь в кашу


    А может подойти к вопросу с другой стороны ?
    1. Научиться пользоваться Class Explorer.
    2. Увеличить mental power skill.

    PS: D5 unit Excel2000  - 65957 строк/5 032 718 байт - каши нет :)
  • Ega23 © (07.05.08 10:13) [3]
    Есть интерфейсы.
  • Ins © (07.05.08 10:38) [4]

    > Напихивание кода в один модуль для того, чтобы была возможность
    > видеть поля класса, меня не устраивает, так как при этом
    > модули разрастаются, превращаясь в кашу. А делать поля класса
    > общедоступными тоже не хочется.


    Законный способ - размещение в одном модуле. Что-то мне кажется, что такое количество друзей у класса, что в модуле получится каша - это признак плохой архитектуры
  • Vladix (07.05.08 10:50) [5]

    > А что, без этой самой "дружественности" уж совсем никак
    > не обойтись ? На ней прямо-таки свет клином сошелся ?


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


    > Есть интерфейсы.

    Замечательно, что они есть )) и пользоваться ими я умею. Но в данном случае это мало чем помогает: все методы интерфейсов - открытые, какой тут может быть разговор об инкапсуляции?
  • Ins © (07.05.08 10:55) [6]

    > Но в данном случае это мало чем помогает: все методы интерфейсов
    > - открытые, какой тут может быть разговор об инкапсуляции?
    >

    С интерфейсами просто можно поступить хитро. Закрыть нужные методы класса в приват и сделать их реализациией интерфейса. Тогда клиентский код сможет получить доступ к этим методам только запросив интерфейс. Запрос интерфейса - это будет своего рода "код доступа" и подтверждение, что клиент знает, что делает. Ну а еще уже в самом коде запроса интерфейса можно сделать некую хитрую проверку на то, имеет ли данный код право на доступ к методам.
  • Юрий Зотов © (07.05.08 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 © (07.05.08 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 © (07.05.08 11:24) [9]
    Хотя нет, так не получится. Клиент, когда запросит интерфейс у объектной ссылки, а не у интерфейсной, это произойдет напрямую через GetInterface, минуя QueryInterface. Так что либо скармливать клиенту интерфейсную ссылку вместо объектной, либо я еще покурю, и что-либо придумаю :)
  • Игорь Шевченко © (07.05.08 11:38) [10]

    > Подскажите, пожалуйста, есть ли в Delphi аналоги Си-шных
    > friend-классов?


    friend-ы в С++ сделаны от безысходности. Аналогов в Delphi нет, потому что они не нужны.
  • Ins © (07.05.08 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 © (07.05.08 11:56) [12]
    function TProtectedObject.GetIntfImpl: IMyInterface;
    begin
    if FAllowAccess then
      Result := FIntfImpl
    else
      Result := nil; // Тут на самом деле лучше кинуть исключение InvalidTypecast
    end;

  • Ins © (07.05.08 12:24) [13]

    > friend-ы в С++ сделаны от безысходности. Аналогов в Delphi
    > нет, потому что они не нужны.


    Не думаю, что от безысходности, они таким образом просто хотели задать более гибкий механизм управления видимостью, чем это делается обычным образом с помощью директив private/protected/public. Гибкий то он действительно более гибкий, но вот то, что сомнительный - это точно.
  • Vladix (07.05.08 12:26) [14]

    > Ins ©

    Код совсем получился неудобочитаемым.. но самое главное - идея, и она понятна. Спасибо за предложенный вариант!


    > Игорь Шевченко ©   (07.05.08 11:38) [10]
    > friend-ы в С++ сделаны от безысходности. Аналогов в Delphi
    > нет, потому что они не нужны.

    Ответ, достойный магистра.. Если денег нет, значит они не нужны )))

    Что нет в C++ сравнительно с Delphi, чтобы Страуструпу от безысходности пришлось в спешном порядке придумывать "дружественность"? Доступа "всему ко всему" в рамках одного модуля? Если так, то "дружественность" выглядит симпатичнее и по крайней мере объявляется в тексте модулей.
  • Игорь Шевченко © (07.05.08 12:47) [15]
    Vladix   (07.05.08 12:26) [14]


    > Что нет в C++ сравнительно с Delphi


    Модульности
  • Юрий Зотов © (07.05.08 16:50) [16]
    > Ins ©   (07.05.08 11:11) [8]

    > Полагаю, это не совсем то.

    Что и было сказано - "в некотором роде".
Есть новые Нет новых   [134491   +8][b:0][p:0.003]