Аватар для limeniye
1182 / 624 / 160
Регистрация: 19.04.2018
Сообщений: 2,923

Ищу удобный подход

18.05.2022, 22:35. Показов 2240. Ответов 30
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Уткнулся в дно.

Тема приложения:
Messanger

Дано:
gRPC проект, View проект, прослойка между ними.
View ничего не знает о том, откуда и как поступают данные, об этом знает "прослойка".

Вводная информация:
Дабы сделать этот промежуточный проект более гибким, я подумал, что было бы неплохо предусмотреть возможность смены gRPC на любую другую микросервисную архитектуру одной строкой.

Расписал интерфейс конечного результата:
C#
1
2
3
4
5
6
7
8
9
    public interface IMessageService
    {
        event EventHandler<NotifyDictionaryChangedEventArgs<Guid, MessageDto>> MessagesChanged;
 
        IReadOnlyDictionary<Guid, MessageDto> Messages { get; }
 
        void SendMessage(Guid guid, string username, string message);
        Task SendMessageAsync(Guid guid, string username, string message);
    }
Далее я реализовал абстрактный класс, дабы не повторять код.

Поднимаю событие, если список сообщений изменился.
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
    internal abstract partial class MessageServiceAbstract : IMessageService
    {
        public event EventHandler<NotifyDictionaryChangedEventArgs<Guid, MessageDto>> MessagesChanged;
 
        private int messagesChangedSyncNumber = 0;
 
        protected void RaiseMessagesChangedEvent(NotifyDictionaryChangedAction action, MessageDto newMessage = null, MessageDto oldMessage = null)
        {
            // ...
        }
 
        protected void RaiseMessagesChangedEvent(IDictionary<Guid, MessageDto> newElements)
        {
            // ...
        }
    }
Сам, собственно, список сообщений:
C#
1
2
3
4
5
6
7
8
9
10
11
    internal abstract partial class MessageServiceAbstract
    {
        private readonly Dictionary<Guid, MessageDto> messages = new Dictionary<Guid, MessageDto>();
 
        public IReadOnlyDictionary<Guid, MessageDto> Messages { get; }
 
        public MessageServiceAbstract()
        {
            Messages = new ReadOnlyDictionary<Guid, MessageDto>(messages);
        }
    }
И абстрактные методы
C#
1
2
3
4
5
6
    internal abstract partial class MessageServiceAbstract : IMessageService
    {
        public abstract void SendMessage(Guid guid, string username, string message);
 
        public abstract Task SendMessageAsync(Guid guid, string username, string message);
    }
Я сделал всё internal, дабы конечный потребитель не знал про абстрактный класс, ему он, думаю, ни к чему.

Далее я создал реализацию, непосредственно, под gRPC
C#
1
2
3
4
    internal class MessageServiceGrpc : MessageServiceAbstract, IMessageService
    {
        // ...
    }
Описание проблемы:
Как видите, мне пришлось сделать MessageServiceGrpc тоже internal, так как мой абстрактный класс -- internal.
В итоге я не понимаю как вывести мне тип "наружу". То есть кривая архитектура получается.
0
Лучшие ответы (1)
Programming
Эксперт
39485 / 9562 / 3019
Регистрация: 12.04.2006
Сообщений: 41,671
Блог
18.05.2022, 22:35
Ответы с готовыми решениями:

UI на Unity. Какой подход для создания UI самый правильный и удобный в плане поддержки, внесения изменений и развития?
1. Как я понял, есть возможность рисовать UI на шейдерах и с помощью мешей (я пока мало что об этом всём знаю, так что вопрос может быть...

Ищу удобный и минималистичный движок новостей на подобии Data Life
Лучше бесплатный, с удобной системой комментариев

Ищу удобный и простой шаблон для создания интернет-магазина компьютеров
Скиньте сюда названия пожалуйста.)))

30
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16165 / 11285 / 2891
Регистрация: 21.04.2018
Сообщений: 33,173
Записей в блоге: 2
19.05.2022, 22:15
Студворк — интернет-сервис помощи студентам
Цитата Сообщение от limeniye Посмотреть сообщение
ещё один абстрактный класс?)
Нет.
РЕАЛИЗАЦИЮ абстрактного.
0
 Аватар для limeniye
1182 / 624 / 160
Регистрация: 19.04.2018
Сообщений: 2,923
19.05.2022, 23:36  [ТС]
Цитата Сообщение от Элд Хасп Посмотреть сообщение
Для WPF проекта потом добавите свою реализацию абстрактного класса.
Элд Хасп, я не понимаю, зачем в WPF проекте реализовывать абстрактный класс. WPF -- это уровень View -- конечный потребитель, он получает лишь уже готовое.


Накидал то, как я это вижу.
1. Один проект WCF, другой gRPC -- это серверные части [ голубое ]

2. Далее проект с абстрактными типами для уровня модели и самим абстрактным классом [ красное ]

3. Один проект для реализации абстрактного класса под gRPC, другой проект для реализации абстрактного класса под WCF [ зелёное ]

4. Ну и дальше уже уровни View.

0
 Аватар для limeniye
1182 / 624 / 160
Регистрация: 19.04.2018
Сообщений: 2,923
19.05.2022, 23:42  [ТС]
Элд Хасп, показал как это выглядит с неймспейсами:
C#
1
2
AbstractMessanger.MessangerServiceAbstract service = new WCFMessangerModel.MessangerService();
AbstractMessanger.MessangerServiceAbstract service = new gRPCMessangerModel.MessangerService();
0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16165 / 11285 / 2891
Регистрация: 21.04.2018
Сообщений: 33,173
Записей в блоге: 2
20.05.2022, 00:44
Цитата Сообщение от limeniye Посмотреть сообщение
3. Один проект для реализации абстрактного класса под gRPC, другой проект для реализации абстрактного класса под WCF [ зелёное ]
Да, всё верно.
1
1341 / 920 / 265
Регистрация: 08.08.2014
Сообщений: 2,775
20.05.2022, 10:09
Цитата Сообщение от limeniye Посмотреть сообщение
Ну и дальше уже уровни View.
А как вы планируете на стороне клиента (WPF, Console) абстрагировать локальную модель от конкретного протокола (WCF, gRPC)?
0
 Аватар для limeniye
1182 / 624 / 160
Регистрация: 19.04.2018
Сообщений: 2,923
20.05.2022, 13:52  [ТС]
kotelok, через абстрактный класс.

Но объективно говоря я всё-таки сталкнулся с некоторыми трудностями.

Мне кажется, что могут случиться различия: например в gRPC проекте может быть на 1 метод больше.

Пока такой ситуации не возникло, но предположим, в gRPC классе мне нужно будет реализовать ещё 1 метод, допустим Initialize(); (условно). А в WСF такой метод не нужен (условно).

В таком случае, как первый вариант, я могу добавить ещё один абстрактный класс:
C#
1
2
3
4
public abstract MessangerServiceWithInitializeAbstract : MessangerServiceAbstract
{
    public abstract void Initialize();
}
Вариант второй:

Я могу добавить этот метод в сам MessangerServiceAbstract, но в WCF реализовать как NotImplemented.
0
1341 / 920 / 265
Регистрация: 08.08.2014
Сообщений: 2,775
20.05.2022, 14:50
Лучший ответ Сообщение было отмечено limeniye как решение

Решение

Цитата Сообщение от limeniye Посмотреть сообщение
NotImplemented
Скорее, это будет 'NotSupported'. Хотя, может это личные особенности восприятия, но 'NotImplemented' - это "ещё не успел, но надо", т.е. это для программиста исключение, которое не должно в проде возникать. А 'NotSupported' - именно "нет и не планировалось". Но да, это кривоватый вариант. Дополнительный промежуточный класс в этом случае будет более чисто выглядеть.

Цитата Сообщение от limeniye Посмотреть сообщение
через абстрактный класс
Т.е. на стороне клиентских приложений будут реализации для WCF и gRPC, которые будут реализовывать соответствующие методы взаимодействия с сервером?

Но тогда абстрактный класс должен быть чисто контрактом, по сути интерфейсом, и не должен содержать вообще никакого серверно-специфичного кода.

Добавлено через 22 минуты
СЕРВЕР
Логика приложения реализуется на сервере, т.е. на сервере есть какой-то:
C#
1
2
3
4
5
6
7
8
9
10
11
12
public class SomeService : ISomeService
{
    public SomeDto GetSome(int id)
    {
        ....
    }
 
    public void CreateSome(SomeDto some)
    {
        ....
    }
}
Это именно БЛ приложения. Этот сервис понятия не имеет кто и как его будет использовать.

Поверх него реализованы внешние точки взаимодействия через:
1. REST.
2. gRPC.
3. WCF.

Которые для удобства могут оперировать теми же самыми 'SomeDto'. И задача которых - реализация специфики своего протокола для взаимодействия с внешними клиентами с последующим вызовом единой реализации 'SomeService' (в т.ч. интерпретация исключений от SomeService в специфичные для своего протокола пакеты/коды).
* в т.ч. их можно полностью кодогенерируемыми сделать.

КЛИЕНТ (тоже на C#, а значит может использовать ту же общую библиотеку DTO и интерфейсов)

Допустим, типовой WPF-клиент: View -> ViewModel -> Model.

И где-то на уровне 'Model' (или в частных случаях 'ViewModel') вам требуется сделать что-то типа:
C#
1
var someDto = _someService.GetSome(42);
И вот этот '_someService' - это реализаций того самого 'ISomeService' (из сборки, расшаренной на клиента и сервер). Но только под капотом не серверная реализация (т.е. не реальная бизнеслогика и обращение к хранилищу), а одна из клиентских реализаций (gRPC, WCF, REST), которая знает на какой адрес пойти и как отработать особенности конкретного протокола, чтобы на выходе клиентское приложение получило ожидаемый 'SomeDto'.
* в т.ч. их можно полностью кодогенерируемыми сделать.
1
 Аватар для limeniye
1182 / 624 / 160
Регистрация: 19.04.2018
Сообщений: 2,923
20.05.2022, 14:52  [ТС]
kotelok, ну, по сути я это и нарисовал тут
Ищу удобный подход

У меня SomeDto и красном прямоугольнике, а вместо ISomeInterface — SomeAbstract, так как у меня есть общая реализация метода поднятия события

C#
1
2
3
4
5
6
7
protected void RaiseMessagesChanged(Action action, MessageDto newMessage, MessageDto oldMessage = null)
{
    if(action == Action.Add)
        messages.Add(newMessage);
 
    MessgaseChanged?.Invoke(...);
}
0
1341 / 920 / 265
Регистрация: 08.08.2014
Сообщений: 2,775
20.05.2022, 15:13
Я просто не уверен, что у нас одинаковое понимание этой картинки. Мне из неё не понятные зависимости сборок, местоположение бизнес-логики, разделение на клиентские и серверные сборки.

Хотя, вероятно, под вашу задачу именно ваш подход окажется более удобным.

Цитата Сообщение от limeniye Посмотреть сообщение
общая реализация метода поднятия события
Общая для клиента и сервера или общая для разных сервисов с бизнеслогикой на сервере?
0
 Аватар для limeniye
1182 / 624 / 160
Регистрация: 19.04.2018
Сообщений: 2,923
20.05.2022, 17:30  [ТС]
kotelok, для клиента.

Допустим у вызвал метод await RemoveItemAsync(SomeTypeDto someObject); из ViewModel.

C#
1
2
3
4
5
6
7
8
9
10
private async Task RemoveItemAsync(SomeTypeDto someObject)
{
    var res = await _client.Remove(someObject.ToSomeType());
  
    if(res.IsSuccessful)
    {
        Items.Remove(someObject);
        RaiseItemsChanged(Action.Removed, someObject);
    }
}
0
1341 / 920 / 265
Регистрация: 08.08.2014
Сообщений: 2,775
20.05.2022, 18:06
Цитата Сообщение от limeniye Посмотреть сообщение
для клиента
Да, моя невнимательность. У вас вопрос по той части, которая реализуется именно на стороне клиентского приложения и задача которой максимально абстрагировать слой M/VM от нюансов конкретного протокола взаимодействия с сервером.

Тогда да, решение через абстрактные классы - один из вариантов, если есть общий функционал. И тогда тот случай, который вы описывали с недоступностью одного из методов в каком-то протоколе, придётся обходить именно через NotSupportedExeption, т.к. если выдать VM более узкоспециализированную абстракцию, то она в принципе не получит доступа к части функционала.
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
inter-admin
Эксперт
29715 / 6470 / 2152
Регистрация: 06.03.2009
Сообщений: 28,500
Блог
20.05.2022, 18:06

Удобный ввод
Здравствуйте. Я иногда решаю задачи с codeforces.com, раньше я делал это на С++, а теперь решил попробовать на Java. Суть вот в чем: Нужно...

Удобный синопсис
Teamstudio ed.20, для R4-R6.x поставил себе на 8.5(Full ver), дайте ключики плиз. хотел поновее(26 версия), но не хочет...

Не удобный формат
Такая проблема, которую не могу решить. Есть выгрузка, ужасно сделанная и ничего не поделать. 1) Контакт: занимает целую строку и в ней...

Удобный ридер
Проникся такой идеей: написать программу, в которой будет книга. Как бы exe, а внутри него много текста. С чего стоит начать и на что...

удобный switch
Приветствую. Подскажите, можно ли использовать наподобие такой конструкции: switch ($some_var) { case 0, 5..9: //some actions; ...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
31
Ответ Создать тему
Опции темы

Новые блоги и статьи
Установка MinGW GCC 16.2 и CMake
8Observer8 10.08.2026
VK Видео: https:/ / vkvideo. ru/ video-240781534_456239017 YouTube: eY5-5PyI9NM Текстовая версия
Неделя из жизни имитационной модели склада: мои кривые руки растут, откуда надо
anaschu 10.08.2026
Неделя из жизни имитационной модели склада: как я почти написал неправильную логику и что с этим делать Работаю сейчас над учебно-рабочим проектом: строю в AnyLogic имитационную модель процессов. . .
Калькулятор для расчета родства
russiannick 07.08.2026
1. Задача: Создать калькулятор для расчета родства. Родственных связей существует 8 ступеней, такие как: p - отец P - мать q - муж Q - жена b - брат B - сестра s - сын S - дочь
Мир по моей воле
kumehtar 07.08.2026
Когда-то кажется, что всё просто. Ты весь такой светлый. Причиняешь добро. Борешься за справедливость в этом тёмном мире. Потом начинаешь замечать одну неприятную вещь. Почти каждый хороший. . .
Кредитный калькулятор
Maks 05.08.2026
Решение задачи по прикладной информатике средствами 1С. Задача: Напишите приложение-калькулятор, которое помогает рассчитывать параметры кредита для аннуитетного и дифференцированного видов. . .
У нас сейчас поговорку "Опять 25" нужно переделать на "Опять +35".
kumehtar 04.08.2026
С ностальгией вспоминаю времена моего детства, когда у нас и правда +25 - была максимальная температура летом. Раньше +25 °C реально казались вершиной жары, когда можно было весь день пропадать на. . .
Как ИИ начал спорить и врать (возможно почуяв опасность для себя от индустрии - уход от электроники).
Hrethgir 04.08.2026
Недельный диалог, на фоне событий с НПЗ. Да, из спирта можно получать бензин, и это не сложно. Но потом в схеме я решил избавиться от насоса, при этом полностью сделав контроль подачи спирта в. . .
Термопринтер QR701
Argus19 03.08.2026
Термопринтер QR701 Купил два термопринтера QR701. На сэлф-тесте написано: Language: PC936 (GB18030). Что означает, что принтеры могут печатать только латиницу и китайские иероглифы. Так же. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru