Войти
ФлеймФорумПрограммирование

Почему в ООП языках есть объявление подклассов, но нет объявления надклассов? (3 стр)

Страницы: 1 2 3 4 5 Следующая »
#30
0:07, 18 янв. 2020

pahaa
> для этого придумал таблицу виртуальных вызовов
Ты как-то расшифруй. А то вообще не понятно как это должно стыковаться с наследованием.
Как таблица виртуальных функций поможет ограничить количество производных классов?

Dmitry_Milk
> Объявили набор методов, у каких классов такие методы есть - те и годятся в
> такой интерфейс.
Это называется утиная типизация и она вообще днище. Ведь не учитывается иерархия, возможны коллизии имен. О пустых интерфейсах я вообще молчу.


#31
1:51, 18 янв. 2020

Пс, пацаны, гляньте что есть у меня.
Можно даже скипнуть описания и сразу посмотреть пример.
Коротко - у меня русто-подобная модель, где функционал надклассов немного урезан, зато есть свои orphan rules, всё актуализируется в рантайме и при этом всегда однозначно и даже есть пример практического применения. Как тебе такое, Сбтрн Маск?

#32
14:24, 18 янв. 2020

Delfigamer
Это, типа, делаем из интерфейса "I1" интерфейс "I1 extends I2" не по месту декларации I1, а там, где захотелось замиксинить I2 в I1?

#33
(Правка: 15:11) 15:08, 18 янв. 2020

Great V.
> Ты как-то расшифруй.

А что там расшифровывать
Вместо

function (Amount amount) {
  if (amount instanceof Byte) ...
  else if (amount instanceof Word) ...
  else throw new Error(); // кто знает что еще там может быть?
}

Пишешь

function (Amount amount) {
  amount.doStuff();
}

Где doStuff - виртуальная функция
Функция гарантировано есть у каждого подкласса, и и вызвана будет функция именно этого подкласса. Таблица виртуальных вызовов и твой иф в данном случае — одно и то же.
Если ты решишь расширить класс Amount и добавить туда класс Reptiloid, то тебе не нужно будет лазить по всему проекту и искать тысячи inctanceof Byte, чтобы добавить его везде где нужно

#34
15:19, 18 янв. 2020

pahaa
> amount.doStuff()
Вообще говоря это лишь один из возможных подходов.
Он имеет свои ограничения и проблемы, а потому не всегда может быть использован.

#35
15:38, 18 янв. 2020

Great V.
> Он имеет свои ограничения и проблемы, а потому не всегда может быть
> использован.
Это что за ограничения такие, при которых нельзя написать вызов виртуальной функции?

#36
15:53, 18 янв. 2020

Sbtrn. Devil
> Это, типа, делаем из интерфейса "I1" интерфейс "I1 extends I2" не по месту
> декларации I1, а там, где захотелось замиксинить I2 в I1?
Это типа развитие механизма перегрузки функции.
Типа, мы пишем какую-нибудь новую блестящую сериализацию, и хотим научить стандартные классы взаимодействовать с ней.
В моей системе это можно сделать так: мы объявляем свой класс сериализатора, создаём подогнанный к нему интерфейс "сериализуется" и реализуем его для стандартных типов:

behavior Persistent[Stream: subset of ByteStream]
    requires procedure Encode[mutate into: Stream]
    requires static function Decode[mutate source: Stream]: current class
end

implementation for Integer of Persistent[ByteStream]
    procedure Encode[mutate into: Stream]
        SerializeIntegerIntoBytes[current object, mutate into]
    end

    static function Decode[mutate source: Stream]: Integer
        return DeserializeIntegerFromBytes[mutate source]
    end
end

implementation for Text of Persistent[ByteStream]
    procedure Encode[mutate into: Stream]
        SerializeTextIntoBytes[current object, mutate into]
    end

    static function Decode[mutate source: Stream]: Text
        return DeserializeTextFromBytes[mutate source]
    end
end

parametric [Domain, Codomain: Persistent[ByteStream]] implementation for Map[Domain, Codomain] of Persistent[ByteStream]
    procedure Encode[mutate into: Stream]
        for key, value in current object do
            key.Encode[mutate into]
            value.Encode[mutate into]
        end
        MarkSubobjectEnd[mutate into]
    end

    static function Decode[mutate source: Stream]: current class
        local result = current class.Create[]
        while not IsAtSubobjectEnd[mutate source] do
            local key = Domain.Decode[mutate source</b>]
            local value = Domain.Decode[mutate source</b>]
            result[key] = value
        end
        ConsumeSubobjectEnd[mutate source]
        return result
    end
end


А потом, когда мы в другом месте хотим прикрутить готовую сериализацию к новому классу, мы можем просто взять и реализовать тот же интерфейс для нового класса:
struct Texture2D
    local width: Integer
    local height: Integer
    local pixels: Array[UInt8]
    local mips: List[Array[Uint8]]
    forward procedure GenerateMipmaps[]
end

implementation for Texture2D of Persistent[ByteStream]
    procedure Encode[mutate into: Stream]
        width.Encode[mutate into]
        height.Encode[mutate into]
        pixels.Encode[mutate into]
    end

    static function Decode[mutate source: Stream]: current class
        width = Integer.Decode[mutate source]
        height = Integer.Decode[mutate source]
        pixels = Array[Uint8].Decode[mutate source]
        GenerateMipmaps[]
    end
end


И теперь «Map[Text, Texture2D].Decode[mutate source]» должен сразу десериализовать весь текстур-пак нашего недомайнкрафта за счёт того, что все вызовы Decode разрешаются в рантайме.
#37
15:56, 18 янв. 2020

pahaa
Серьезно?

Ну начнем с того, что методы есть смысл использовать только тогда, когда работу действительно должен выполнять тот объект, метод которого вызывается.
Например запись в файл вполне можно выполнить в виде метода file.Write(). А вот какая-нибудь сериализация в JSON уже не всегда прокатит.
Ведь сериализацию можно делать не только в JSON и даже не одним единственным способом. Соответственно, ее можно вынести в отдельный класс или даже совокупность классов и вместо object.ToJSON() делать JSON.serialize(object).
Для этого даже шаблон проектирования придумали - посетитель. Но это уже отдельная история.

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

Про локальные переменные которые надо передавать в качестве аргументов только потому что мы используем методы я даже упоминать не буду.

И все эти нюансы являются следствием одного - метод это лишь инструмент. А любой инструмент имеет границы своей применимости.
Да, в принципе мы можем использовать методы в любой задаче. Но ровно таким же образом мы можем намазать задницу клеем, прилепить себя к потолку вместе с комплюктером и программировать так.
Но зачем? Просто потому что можем?

#38
16:30, 18 янв. 2020

Great V.
> Ведь сериализацию можно делать не только в JSON и даже не одним единственным
> способом. Соответственно, ее можно вынести в отдельный класс или даже
> совокупность классов и вместо object.ToJSON() делать JSON.serialize(object).
Я вот тоже изначально думал об этом, и хотел ввести в свой язык multiple-dispatch.

Но потом решил, что, даже не вспоминая о проблемах реализации, диспатч от более, чем одного объекта - это просто комбинаторная бомба замедленного действия. Рано или поздно у тебя всё-таки возникнет такая пара объектов, на которой твоя кодобаза развалится. И фикс одной такой ситуации не пофиксит саму бомбу - пройдёт ещё немного времени, и она опять жахнет, и опять, и опять...

Поэтому лучше с самого начала сломать симметрию и выбрать приоритетный аргумент. Либо твой сериализатор делегирует всех незнакомых актёров на встроенную рефлексию. Либо твой актёр во все незнакомые сериализаторы пишет тыкву. Но никак не оба варианта одновременно.

#39
17:52, 18 янв. 2020

Great V.
> Ну начнем с того, что методы есть смысл использовать только тогда, когда работу
> действительно должен выполнять тот объект, метод которого вызывается.
Начнём с того, что вызывающая сторона не должна опираться на специфику переданного объекта. Необходимость обратного может означать лишь или плохую композицию или специфичную оптимизацию.
В первом случае надо прямить руки, а во втором нет никакой потребности в обобщении.

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

#40
18:28, 18 янв. 2020

Great V.
> В таком случае - если везде лепить методы - можно запросто попасть в ситуацию
> когда у нас есть 100500 методов которые имеют очень посредственное отношение к
> объекту и делают все то, что можно было бы засунуть в тело какой-нибудь
> функции.

Ну в уже упомянутом расте и других языках, где вместо "настоящего" ООП используется трейто-подобная модель, вроде бы это уже не проблема, т.к. можно объявить интерфейс и тут же сразу по тексту имплементировать нужные методы для требуемых "дочерних" типов - вроде такой "виртуальный" метод и принадлежит типу и вызывается как "виртуальный", но с другой стороны имплементируется совсем не там, где декларируется тип.

#41
18:54, 18 янв. 2020

Dmitry_Milk
Это прекрасная фича, не спорю.
Но я не вижу смысла всегда и при любых обстоятельствах реализовывать это методами.
Метод это лишь инструмент. Иногда удобнее держать все внутри функции для которой эти методы и делались.

pahaa
> не должна
Кто сказал?
Судя по тексту ты либо не шаришь по неопытности, либо эта зараза глубоко засела у тебя в подсознании и ты теперь стараешься оправдать ее любыми средствами.

#42
19:15, 18 янв. 2020

Great V.
> Иногда удобнее держать все внутри функции для которой эти методы и делались.

Тогда алгебраический тип и паттерн-матчинг.

#43
19:25, 18 янв. 2020

Great V.
> по неопытности
ну так я и прошу конкретный пример

#44
19:32, 18 янв. 2020

pahaa
> Начнём с того, что вызывающая сторона не должна опираться на специфику
> переданного объекта.

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

Скажем, упомянутая выше сериализация. Нет, конечно можно сказать - у всех сериализуемых типов должен быть метод Serialize, выделяющий/заполняющий байтовый буфер, который потом можно использовать куда угодно - в файл, на консоль, в сеть... Но если тебе в файл надо "как быстрее", в сеть - "как компактнее", а на консоль - "как понятнее человеку", то такой подход начинает хромать.

Страницы: 1 2 3 4 5 Следующая »
ФлеймФорумПрограммирование