Есть в Ruby одно заметное нарушение LSP: класс, будучи вообще-то модулем (Class
— прямой наследник Module
), не может быть использован вместо модуля в качестве примеси, т.е. в “include
” или “extend
”. Это, в принципе, обоснованно, поскольку они не только объекты, но и конструкции языка...
Желание подмешивать классы, вроде бы, и не должно возникать, но есть распространенная ситуация, когда хочется — когда мы хотим получить собственные методы подмешанного модуля как методы класса. Поясню кодом:
module Alpha def alpha end class << self def alpha2 end end end class Beta include Alpha end
Так вот, метода Beta.alpha2
мы таким образом не получим, хотя если бы Alpha
был классом и использовалось бы наследование, метод класса унаследовался бы ничуть не хуже метода экземпляра. Так и тянет выполнить что-нибудь вроде “Beta.extend(Alpha.singleton_class)
”, но нельзя.
Решение, безусловно, достаточно очевидное — вынести соответствующий функционал в отдельный модуль и подмешать его как к классу, так и к основному модулю (если нужно). А еще лучше — подмешивать автоматически при включении основного модуля. Т.е. получается следующее:
module Alpha module Cls def alpha end end extend Cls class << self def included target target.extend Alpha::Cls end private :included end end class Beta include Alpha end
Повторюсь: решение очевидное, и, возможно, не стоило б о нем писать, если б не одно «но» — очевидно оно именно при такой постановке задачи, тогда как в реальности до этого еще дойти надо. Что сделать гораздо проще, если помнить о существовании такого паттерна.
PS. Кстати, в процессе обдумывания обнаружено еще одно нарушение LSP: класс Class
не совсем обычный — от него нельзя создать наследника (от Module
можно).
Комментариев нет:
Отправить комментарий