Pull to refresh

Comments 20

Такое ощущение, что статья из 2009 года. — .net 3.5 как-то не очень свежо, особенно для 2014 года.

Сейчас скорее какой-нибудь SignalR для чатов на .net модно (без звука, видео и файлов)
Ну статья и не про .NET. И не про новые технологии, а про чат. Мне хотелось что бы он работала на win xp.
Мне хотелось что бы он работала на win xp.

Такое ощущение, что статья из 2009 года.

Эхх, помню как я писал свой чат на Delphi.
И TCP клиент/сервер, и чат ВК (через старые API) используя IndyHTTP, и всякое прочее:
habrastorage.org/files/3ca/96a/bb2/3ca96abb2e0441fcbce4ee32f24a9f68.jpg
habrastorage.org/files/d4c/aa6/53f/d4caa653f900495d92a7b37513edab42.png

Времена… Чаты…
Раньше и трава зеленее была… Эххх…
Гы, я не один такой :)

Только без клиент-сервера, все клиенты были равноправны. Уж не помню, как точно работало, но это был велосипед по типу p2p. Даже аватарки поддерживал! Был 2004 год :)
А я вообще себя старым почуствовал. В 2002м свой первый клиент->сервер чат на VB6 с сетью через winsocks ActiveX-контрол. При этом сдувая код с различных ресурсов, не особенно стесняясь. И потом вставляя свой «копирайт» :) Эх время!
Напоминает FizzBuzz Enterprise Edition.

Довольно простая и наглядная задача была, но в данном исполнении актуальный код, делающий работу, затерян в кишках огромного монстра. Я еле нашёл то место, где осуществляется передача части файла через P2P, причём место, где происходит контроль и вычисляются проценты — не нашёл. Вы уверены, что сможете за адекватное время прикрутить к этому шифрование, если вас внезапно попросят?

Не tree, а лес дремучий
$ tree Engine/
Engine/
├── API
│   ├── IClientAPI.cs
│   ├── IClientAPICommand.cs
│   ├── IServerAPI.cs
│   ├── IServerAPICommand.cs
│   └── StandardAPI
│       ├── BaseCommand.cs
│       ├── ClientCommands
│       │   ├── ClientCommand.cs
│       │   ├── Files
│       │   │   ├── ClientFilePostedCommand.cs
│       │   │   ├── ClientPostedFileDeletedCommand.cs
│       │   │   ├── ClientReadFilePartCommand.cs
│       │   │   └── ClientWriteFilePartCommand.cs
│       │   ├── Messages
│       │   │   ├── ClientOutPrivateMessageCommand.cs
│       │   │   ├── ClientOutRoomMessageCommand.cs
│       │   │   └── ClientOutSystemMessageCommand.cs
│       │   ├── Others
│       │   │   ├── ClientEmptyCommand.cs
│       │   │   ├── ClientPingResponceCommand.cs
│       │   │   └── ClientReceiveUserOpenKeyCommand.cs
│       │   ├── P2P
│       │   │   ├── ClientConnectToP2PServiceCommand.cs
│       │   │   ├── ClientConnectToPeerCommand.cs
│       │   │   └── ClientWaitPeerConnectionCommand.cs
│       │   ├── Registrations
│       │   │   └── ClientRegistrationResponseCommand.cs
│       │   ├── Rooms
│       │   │   ├── ClientRoomClosedCommand.cs
│       │   │   ├── ClientRoomOpenedCommand.cs
│       │   │   └── ClientRoomRefreshedCommand.cs
│       │   └── Voice
│       │       └── ClientPlayVoiceCommand.cs
│       ├── ServerCommands
│       │   ├── BaseServerCommand.cs
│       │   ├── Files
│       │   │   ├── ServerAddFileToRoomCommand.cs
│       │   │   └── ServerRemoveFileFormRoomCommand.cs
│       │   ├── Messages
│       │   │   ├── ServerSendPrivateMessageCommand.cs
│       │   │   └── ServerSendRoomMessageCommand.cs
│       │   ├── Others
│       │   │   ├── ServerEmptyCommand.cs
│       │   │   ├── ServerGetUserOpenKeyCommand.cs
│       │   │   └── ServerPingRequestCommand.cs
│       │   ├── P2P
│       │   │   ├── ServerP2PConnectRequestCommand.cs
│       │   │   └── ServerP2PReadyAcceptCommand.cs
│       │   ├── Registrations
│       │   │   ├── ServerRegisterCommand.cs
│       │   │   └── ServerUnregisterCommand.cs
│       │   ├── Rooms
│       │   │   ├── ServerCreateRoomCommand.cs
│       │   │   ├── ServerDeleteRoomCommand.cs
│       │   │   ├── ServerExitFormRoomCommand.cs
│       │   │   ├── ServerInviteUsersCommand.cs
│       │   │   ├── ServerKickUsersCommand.cs
│       │   │   ├── ServerRefreshRoomCommand.cs
│       │   │   └── ServerSetRoomAdminCommand.cs
│       │   └── ServerCommand.cs
│       ├── StandardClientAPI.cs
│       └── StandardServerAPI.cs
├── Audio
│   ├── IPlayer.cs
│   ├── IRecorder.cs
│   └── OpenAL
│       ├── OpenALPlayer.cs
│       └── OpenALRecorder.cs
├── Containers
│   ├── RequestPair.cs
│   ├── WaitingCommandContainer.cs
│   └── WaitingPrivateMessage.cs
├── Engine.csproj
├── EventArgs
│   ├── AddressReceivedEventArgs.cs
│   ├── AsyncErrorEventArgs.cs
│   ├── ConnectEventArgs.cs
│   ├── DataReceivedEventArgs.cs
│   ├── DataSendedEventArgs.cs
│   ├── FileDownloadEventArgs.cs
│   ├── ReceiveMessageEventArgs.cs
│   ├── RecordedEventArgs.cs
│   ├── RegistrationEventArgs.cs
│   └── RoomEventArgs.cs
├── Exceptions
│   ├── ErrorCode.cs
│   └── ModelException.cs
├── Helpers
│   ├── Crypter.cs
│   └── Logger.cs
├── Model
│   ├── Client
│   │   ├── ClientContext.cs
│   │   └── ClientModel.cs
│   ├── Common
│   │   └── ModelContext.cs
│   ├── Entities
│   │   ├── DownloadingFile.cs
│   │   ├── FileDescription.cs
│   │   ├── PostedFile.cs
│   │   ├── Room.cs
│   │   ├── RoomType.cs
│   │   ├── SoundPack.cs
│   │   ├── User.cs
│   │   └── VoiceRoom.cs
│   └── Server
│       ├── ServerContext.cs
│       └── ServerModel.cs
├── Network
│   ├── AsyncClient.cs
│   ├── AsyncPeer.cs
│   ├── AsyncServer.cs
│   ├── Conections
│   │   ├── Connection.cs
│   │   ├── IConnection.cs
│   │   └── ServerConnection.cs
│   └── P2PService.cs
└── Properties
    └── AssemblyInfo.cs

31 directories, 89 files
Наверно потому, что проценты эта задача UI. И модель не должна заботится о том, что бы высчитывать для него проценты и все остальное. А они как и положено находятся в MessageViewModel.

На счет шифрования не уверен.
Согласен с вами, что отображение процентов это задача UI. Но я спрашивал, где вы вычисляете проценты, то есть делите часть на целое и умножаете на 100.
А они как и положено находятся в MessageViewModel.
А вот здесь вы не правы, проценты у вас считаются в файле Engine/API/StandardAPI/ClientCommands/Files/ClientWriteFilePartCommand.cs#L71

downloadEventArgs.Progress = (int)((downloadingFile.WriteStream.Position * 100) / receivedContent.File.Size);

и потом всплывают, как вы верно заметили, в MessageViewModel.cs#L147

    private void ClientDownloadProgress(object sender, FileDownloadEventArgs e)
    {
      roomViewModel.MainViewModel.Dispatcher.Invoke(new Action<FileDownloadEventArgs>(args =>
      {
          .....
          if (args.Progress >= 100)

Ну вы и без меня всё это прекрасно помните.
Ага ошибся так ошибся. Плохо что они там считаются, надо бы их от туда убрать.
Почти сотня файлов… боже, это точно не межгалактический мессенджер на ближайшие тысячу лет? :) Какое ужасно громоздкое решение.
Вопрос на засыпку: а что вам дало применение MVVM? Что вот этот двухоконный чат выйграл от «интыпрайз» архитектуры?
То что UI это не 1 файл размером в 2000 строк. (изначально он такой и был)
Во вторых удобный механизм привязок и шаблонов, который упростился при переходе на MVVM.

Ну и мне удобней работать в 100 маленьких файлах, чем в 10 огромных.
Ну допустим файл с формой уменьшился (хотя вам-то что? Он всё-равно отформатирован, видно что и где). Но код-то никуда не пропал — вы выделили отдельную сущность и потратили время ещё и на взаимодействие V с VM. И кому стало легче от трёх сущностей вместо двух?
Про «привязок и шаблонов» — они здесь каким боком? Binding был ещё в WinForms, а шаблоны — те тоже как бы к MVVM отношения не имеют — всего лишь «фича» WPF.
Разделяй и властвуй :)
Намного проще писать программу когда все структурно разделено, и не нужно тратить время на поиск информации в огромном методе, или огромном классе маленький метод. Ну если вам легче от разделения когда не становится, это лично ваше мнение. Мне, как я уже говорил, проще работать именно так.

И куда, кстати, вы собрались привязываться если не установите DataContext view'y? В таком случае вам в ручную придется задавать свойства всем корневым контролам. В случае с MVVM задается DataContext, а все остальное (установление, обновление свойств и списков) делает View. За счет этого и упрощение. Это из того что быстро вспомнилось.
Вы смешиваете в кучу просто неструктурированный код и нормальный, модульный код, но без MVVM наворотов. в вашем случае вы имеете тривиальную задачу доставки сообщений. Ну, пяток классов я бы там накидал, показал на формах — MVVM там делать нечего.

И куда, кстати, вы собрались привязываться если не установите DataContext view'y?


Туда же, куда и в обычной двухуровневой схеме — к модели. Скажем, класс Message — он спокойно покажется в списке и без отдельного MessageView.

Для ясности вам-бы как-то уяснить: WPF — это НЕ MVVM! На WPF можно писать под любую архитектуру.
Вы смешиваете в кучу просто неструктурированный код и нормальный, модульный код, но без MVVM наворотов. в вашем случае вы имеете тривиальную задачу доставки сообщений. Ну, пяток классов я бы там накидал, показал на формах — MVVM там делать нечего.


Хорошо, напишите, со всем функционалом который есть у меня. Тогда мы сможем поговорить уже опираясь на код, а не на «а я бы его...».

На WPF можно писать под любую архитектуру

Я что, спорю что нельзя? Я говорю, что привязки и шаблоны наиболее удобно использовать с MVVM. Про уяснить я вообще промолчу.

За примерно год существования этого чата, я ни разу не пожалел что сделал «монстра». Т.к. новый функционал, вроде голосового чата, P2P и т.д. отлично лег в существующую архитектуру.
Тратить кучу времени, чтобы только показать, что 90 файлов проекта в «гиперархитектуре» MVVM — это overdesign? Нет, спасибо — я оставлю вас при вашем апломбе.

Я говорю, что привязки и шаблоны наиболее удобно использовать с MVVM


Чушь, эти вещи вообще никак не соотносятся. Можно иметь просто «модель», привязанную (мы же про binding?) к форме. Вы понимаете о чём я говорю? Шаблоны (мы про шаблоны WPF: item template, data template — про них ведь?) — аналогично, НИКАК не соотносятся какую архитектуру вы выбрали.
MVVM — просто модель программы, шаблонам всё равно как вы называете низлежащие классы. Просто MV* — современный баззворд, под который модно приплетать все технологии. :) (хотя он полезен, не спорю)

Я допускаю, что вы сделали отличную архитектуру, но я не вижу особых преимуществ от использования MVVM — «чатик» мне не кажется тем «сложным энтерпрайзом», где оправдан такой трёхуровневый подход — можно и в два уровня спокойно уложить архитектуру с ничуть не меньшей гибкостью и расширяемостью — весь вопрос лишь в опыте.
Простота — залог удобства для тех, кто решит развивать проект. Мне не страшны эти VM/M, но они явно усложняют работу с кодом без объективной надобности.

Когда оправдан MVVM? Когда есть жёстко очерченный, ограниченный backend, куда можно сохранять только примитивные классы типа POCO или, например, когда model невозможно обвешать атрибутами, специфичными для persistence технологий (EF, XML, etc). Тогда ПРИХОДИТСЯ генерировать ViewModel, адаптирующую «простые, неуклюжие модели» к rich UI. То есть это не ради какой-то «гипергибкости», а тупо в силу ограничений на модель.
У вас model — тупо «сообщения», никем и ничем не ограниченные, создаваемые вами же, т.е. практически любой формы. На этом фоне играть в MVVM — это стрелять из коллайдера по пивным пробкам. Это моё мнение, я не говорю что у вас «всё плохо» — у вас оно НЕ ОПРАВДАНО.
Sign up to leave a comment.

Articles