С++ Абстракция ООП (наследовать только некоторые методы производного класса)

Предположим, у меня есть класс сокета:

class Socket{
public:
    ... Some Code ...
    Socket(int type){
         isServer = type;
        //some code
    }
    virtual void Send(string s);
    virtual void Send(string s, int clientID);
    ... Some Code ...
private:
    int isServer;
};

Это должно использоваться как в качестве сервера, так и в качестве клиента.

Теперь мне нужно иметь 2 производных класса:

class ClientSocket : public Socket{
public:
    ... Some Code ...
};

class ServerSocket : public Socket{
public:
    ... Some Code ...
};

Я хочу, чтобы ClientSocket имел доступ только к Send(string), а серверный сокет имел доступ только к Send(string, int).

Я проверил другой ответ:

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


person Jaysmito Mukherjee    schedule 26.05.2021    source источник
comment
ClientSocket будет иметь доступ только к... звучит как свойство времени компиляции, но вызовы виртуальных функций отправляются во время выполнения.   -  person Evg    schedule 26.05.2021
comment
Мне кажется, что эти функции Send не должны быть частью базового класса Socket, а вместо этого должны быть унаследованными классами.   -  person Some programmer dude    schedule 26.05.2021
comment
@Evg не компилирует время, если вы идете на наследование, я думаю. и для виртуальных функций я должен сделать это, так как код реализован в другом месте, чтобы сделать его независимым от платформы   -  person Jaysmito Mukherjee    schedule 26.05.2021
comment
Тогда что есть доступ только к...? Что должно произойти, если вы пытаетесь получить доступ, а доступа не должно быть? Ошибка компиляции, исключение, завершение, ...?   -  person Evg    schedule 26.05.2021
comment
@Someprogrammerdude Да, но я хочу иметь необработанную версию сокета, имеющую все функции сокета в том же месте, поскольку я пытаюсь быть независимым от платформы, будет очень сложно иметь отдельные реализации для классов diff.   -  person Jaysmito Mukherjee    schedule 26.05.2021
comment
@Evg На данный момент я реализовал ошибку времени выполнения. Но я точно не знаю, что было бы лучше   -  person Jaysmito Mukherjee    schedule 26.05.2021
comment
Если у вас есть сокет и вы не знаете, сервер это или клиент, как вы узнаете, какой посыл использовать? Если вы не можете знать, то наследование не имеет смысла. У вас нет действительных отношений is-a.   -  person user4581301    schedule 26.05.2021
comment
С точки зрения дизайна это не имеет для меня особого смысла. Серверные и клиентские сокеты, по крайней мере, для TCP, ведут себя очень по-разному. Серверный сокет вообще не должен иметь никаких функций отправки или получения, а только прослушивать и принимать операции типа. Операция принятия возвращает объект клиентского сокета, который может отправлять и получать общие данные. Затем у вас может быть контейнероподобный класс, обертывающий несколько объектов клиентских сокетов, и у него может быть функция для отправки во все сокеты или только в один конкретный сокет.   -  person Some programmer dude    schedule 26.05.2021
comment
@user4581301 user4581301 я знаю, что у меня есть isServer   -  person Jaysmito Mukherjee    schedule 26.05.2021
comment
@Someprogrammerdude Здесь я сделал это, пытаясь абстрагироваться от всего сокета (например, это будет то же самое для Windows Linux Mac), и здесь у меня есть метод Accept, который возвращает int (ClientID) и с точки зрения дизайна, я думаю, что я должен сделать Отправить ( string, int) static, так как он вообще не связан с экземпляром сервера   -  person Jaysmito Mukherjee    schedule 26.05.2021
comment
Я хочу сказать, что если вам нужно вызывать isServer или иным образом знать, что у вас есть ServerSocket, то вы почти ничего не выиграете от полиморфизма, так что не делайте этого. Без него программа будет работать эффективнее. Полиморфизм лучше всего проявляется, когда вам не нужно знать точный производный тип. Прямо сейчас вы боретесь против принципа замены Лисков, и это принесет боль, которая только начинается.   -  person user4581301    schedule 26.05.2021
comment
Если вам нужно отслеживать, на какой тип производного класса указывает ваш полиморфный указатель, ваш дизайн, скорее всего, плох. Нет хорошего способа решить эту проблему. Я бы порекомендовал вернуться к чертежной доске и внести некоторые изменения в ваш дизайн.   -  person super    schedule 26.05.2021
comment
@super Думаю, я буду. Я думаю, что я разделю ClientSocket и ServerSocket   -  person Jaysmito Mukherjee    schedule 26.05.2021
comment
Исторический: я сделал почти то же самое около 10 лет назад. Я сильно пожалел об этом примерно через год, когда использовал его в нескольких проектах и ​​попытался портировать еще на несколько ОС. Я пытался убить его, по крайней мере, 8 лет, но он просто не умирает. Мне все время приходится поддерживать вещи, основанные на нем. Эй, чувак, когда ты исправишь corelib::net? Это непоправимо. Прекратите использовать его. Ха-ха-ха-ха-ха. Серьезно, чувак, когда ты исправишь corelib? Это ВЫГЛЯДИТ хорошей идеей, пока вы действительно не начнете ее использовать.   -  person user4581301    schedule 26.05.2021


Ответы (1)


Не существует абсолютного способа сделать это.

Но вот моя идея:

создайте два класса (например, интерфейс):

первый класс - отправитель сокета сервера:

Class ServerSocketSender {
  public:
    virtual void Send(string s, int clientID);
}

второй класс - отправитель клиентского сокета:

Class ClientSocketSender {
  public:
    virtual void Send(string s);
}

из-за принципа сегрегации интерфейса я рекомендую вам сделать это, поэтому не стоит объединять два метода отправки в одном классе и включать метод желания в классе желания.

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

person N0ll_Boy    schedule 26.05.2021
comment
Я уже сказал, что не хочу реализовывать какой-либо другой класс, поскольку эти методы отправки используют некоторый собственный API ОС, поэтому очень сложно иметь для этого разные классы, поскольку это увеличит размер кода и затруднит его обслуживание. - person Jaysmito Mukherjee; 26.05.2021
comment
если вы не хотите использовать дополнительный класс, я рекомендую вам поместить методы отправки в унаследованный класс, а не в базовый класс, потому что они имеют разные сигнатуры и цели, поэтому это неправильно с точки зрения ООП. - person N0ll_Boy; 26.05.2021
comment
Опять же, это нецелесообразно, я также могу напрямую использовать класс Socket. - person Jaysmito Mukherjee; 26.05.2021
comment
используйте SFINAE ссылку - person N0ll_Boy; 26.05.2021
comment
SFINAE — это механизм времени компиляции, но виртуальные вызовы — это механизм времени выполнения. - person Evg; 26.05.2021
comment
@Evg Я не думаю, что нам нужно использовать виртуальные функции. CRTP допускает наследование без виртуальных функций, и все, чего он хотел, это наследование. Или я неправильно понял исходный вопрос? - person Jerry Jeremiah; 26.05.2021
comment
@JerryJeremiah Я в чем-то с тобой согласен. Но из вопроса не понятно, является ли CRTP хорошим решением - слишком мало подробностей о проблеме и требованиях. - person Evg; 26.05.2021