Вдруг, вас осинило...Значения все таки лучше зашифровать от дядей Эдиков...
Ну, что ж поделать, добавим в этот же класс xor предварительно переименовав InputOutputStream в XorInputOutputStream.
Потом, вдруг, ты понял, что было бы неплохо добавить компрессию.
Но что мы получим в итоге?CompressedProtectedInputOutputStream?
Глава 4. в чем проблема данного подхода?
Проблем тут уже ОЧЕНЬ много.Дело в том, что ваш класс уже на данном этапе трансформирует числа в байты, шифрует их, компрессит(!).Это
1) усложняет в миллион раз анализ кода написанного внутри
2) делает абсолютно невозможным переиспользование.ваш проект будет разрастаться в размерах со скоростью света.вы будете городить один и тот же код под одинаковые задачи.
можно продолжать ещё очень долго, но давайте перейдем к SOLID.
Глава 5. SOLID - что это за хуйня?
SOLID - акроним который скрывает в себе 5 принципов которые расшифровываются как:
SRP - принцип единственной ответственности.(single responsibility principle)
OCP - принцип открытости для расширения, закрытости для изменения.(open-closed principle)
LSP - принцип подставления барбары лисков.(не бойтесь названия, это ирл никнейм бабушки)(Liskov substitution principle)
ISP - принцип разделения интерфейса.(interface segregation principle)
DIP - принцип инверсии зависимостей.(dependency inversion principle)
Глава 6. В чем тут нарушение принципов?Что они вообще из себя представляют?
SRP говорит о том, что ваш компонент должен иметь лишь одну ответственность.Как это понять?Роберт Мартин описывает это как одну причину для изменения.И ведь действительно
1) вдруг появится потребность в изменении шифра?
2) вдруг появится потребность в изменении трансформации числа в байты?
3) вдруг появится потребность в изменении компрессии?
Это абсолютно 3 не связанных между собой задачи которые и противоречат принципу единственной ответственности.
Тут важная ремарка, не стоит проводить декомпозицию до максимально возможного предела.Дробить пока дробиться - плохой подход.В адекватном случае в следствии "правильной ответственности" вы получите код который сможете переиспользовать.
Это самый легкий принцип(на первый взгляд, на самом деле он самый трудный), но он убивает миллион зайцев сразу :
ISP
(он затрагивает даже принципы GRASP)
High Cohesion(высокое сцепление) <- говорят что лучше не переводить этот принцип на русский язык.
OCP говорит о закрытости для изменения(старого кода) и открытости для расширения(старого кода).Т.е новую функциональность старым компонентам вы должны вводить наследованием, композицией и тд.
Не стоит брать этот принцип близко к сердцу, разумеется баги нужно фиксить изменением старого кода)
LSP - самый ебанутый принцип который к нам к тому же и не относится.
Его ебанутость заключается в том, что его название не отображает его смысл, отнюдь, он отображает фамилию бабушки которая его придумала.
Заключается он в том, что нельзя менять сигнатуру виртуальных методов при наследовании, добавлять исключения не описанные в начале иерархии.Можете не беспокоиться, хуй вам компилятор даст это сделать в ОО языках на которых пишут читы.
Так же этот принцип описывает то, что компоненты при наследовании должны уметь "встрять" за своего родственника по иерархии.Т.е если метод принимает Button, то MegaSuperPuperButton(extends Button) в любом случае должен корректно выполняться в том же методе.
ISP - разбивай свои интерфейсы по максимуму(также как с SRP.знайте грани декомпозиции).
DIP - с первого взгляда самый трудный и непонятный принцип.Вот его описание с wiki(в книге Роберта Мартина вроде как звучит как то похуже) :
На самом деле он самый легкий относительно его пользы.
Глава 6: Мы нарушили только SRP?
Нет. Мы нарушили все кроме принципа подставления Барбары Лисков.
OCP - мы ввели функциональность изменяя старый код.
ISP - наш класс имеет интерфейс записи, чтения.(относительно спорный момент в данном контексте, но в огромном проекте лучше не выебываться и все таки разбивать IO на две иерархии для переиспользования.Мало ли вашему второму другу кодеру понадобится только Reader?)
DIP - подход изначально был вне инверсии зависимостей.
Глава 7: Перепишем код.
Для начала поподробнее расскажу про DIP, он очень важный в построении любого компонента.Формулировка очень трудная, на самом деле все в разы легче, а импакта миллион.
Dependency
qqqqqq111111 -(повторюсь)
Уровень абстракции - уровень того, насколько близко лежит решение задачи.
Если бы это можно было б визуализировать, я б визуализировал так :
Уровень абстракции уменьшается с увеличением размера стека, где вершина стека всегда является самым низким уровнем абстракции по отношению к элементам которые распологаются ниже по стеку.
Если уж разъяснять на пальцах, то метод где вы вызываете writeInt - высокий уровень абстракции.Ведь вам совсем похуй что там происходит внутри, вы просто пишите несколько символов которые запускают огромную задачу.Для вас это все та же абстракция "writeInt".
То есть место где мы будем юзать наш IO(высокий уровень абстракции) не должен зависить от реализации интерфейса(относительно низкого уровня абстракции), отнюдь, они должны оба зависеть от интерфейса(абстракции).
Компонент A имея потребность в компоненте B описывает интерфейс который ему нужен.компонент B в свою очередь реализует его.Таким образом компонент A зависит от интерфейса, компонент B зависит от интерфейса, а не компонент A зависит от B.
Поэтому это и называют инверсией зависимостей.
Это избавляет нас от конкретной реализации и дает по полной программе насладиться полиморфизмом.
+ Low Coupling из GRASP в подарок.
Также реализуем ISP, разбивая Input и Output на отдельные иерархии.Это даст нам возможность переиспользования в других случаях.
Приступим.
Я предпочту делегирование для реализации компрессии, крипта.
Почему не наследование?Дело в том, что наследование в некоторых случаях(90%) - антипаттерн.Не буду много городить, в двух словах, нарушение инкапсуляции и ещё несколько проблем которые появляются в следствии наследования.Делегирование в данном контексте сыпит нам лишь плюсы которые мы сейчас и увидим.
Во первых имплементируем обычный Input/Output
Тут видны плюсы делегации(объект может становится кем угодно в этапе исполнения программы.наследование разумеется так не может)
а так же видны и наши старания - теперь каждый компонент можно применять отдельно.
Но теперь встает опять вопрос...Видов реализаций так много, будет довольно трудно поддерживать пары input/output.
Давайте это исправим добавив в нашу систему паттерн AbstractFactory.
AbstractFactory позволяет нам описать интерфейс создания объектов связанных по смыслу, но имеющих разные "наборы" реализаций.
Спасибо что прочитали эту огромную статью, на сегодня все.Однозначно смотивирую вас разобраться в этом самостоятельно : то что описано в статье - 1% из того всего что я изучал на счет клинкода.
Помимо этого, добавлю, что вполне где то мог ошибиться/привести неуместный пример.Принимается любая критика.
P.S ко всем классам написаны юнит тесты.Если нужно, прикреплю как метаинформацию.
Bye