Как расширить существующие библиотечные классы? - PullRequest
0 голосов
/ 03 марта 2019
normalize: sum
    | strategy normalizingSum |
    strategy := sum collect: [ :each | each max: 0.0 ].
    normalizingSum := strategy sum.
    ^strategy collect: [ :each |
        normalizingSum strictlyPositive
        ifTrue: [ each / normalizingSum ]
        ifFalse: [ 1.0 / Action subclasses size ]
        ]

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

Из того, что я могу сказать, как правило, большинство методов Pharo работают непосредственно с экземплярами, а в функциональных языкахможно было бы определить функцию, аналогичную статическим методам в C # / Java.

Один из вариантов - поместить метод в метакласс в Pharo, но синтаксис Pharo плохо подходит для этого стиля программирования.Например, у него нет оператора канала, который объясняет сильный наклон к методам экземпляров во всем коде библиотеки, который я наблюдал.

Расширение стандартного библиотечного класса путем непосредственного помещения в него метода кажется немного неправильнымкак-то мне.Когда придет время вносить изменения в Github, как именно все пойдет?Если поместить его прямо в класс Array, то, похоже, со временем он превратится в кошмар версионирования.

Другой выбор - наследовать от Array.Это может быть хорошо для проблемы, которую я делаю сейчас, но позже я хочу сделать другую проблему и не хочу делиться реализациями.

Должен ли я поместить ее в черту и добавить черту в Array * * 1016

Ответы [ 2 ]

0 голосов
/ 03 марта 2019

Проблема здесь в том, что мы рассматриваем только одно сообщение, на которое ваши коллекции должны ответить: #normalized.В вашем коде коллекция sum - это объект, который должен быть нормализован .Поэтому было бы заманчиво сказать sum normalized.Однако я бы не рекомендовал добавлять #normalized к Array (или Collection), потому что логика вашей конкретной нормализации не присуща Array: она зависит от Action, который выглядит как концепция, которая имеет смысл в контексте вашего проекта.

Это не означает, что вы никогда не должны расширять Array своими вещами.Это означает только то, что если ваше расширение не является внутренним, такое решение потребует больше размышлений.

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

Просто в качестве примера давайте предположим, что ваш классназывается StrategyCollection.Вы можете определить в нем

strategy
  ^sum collect: [:each | each max: 0.0].

, где sum теперь является иваром вашего класса.Затем вы можете определить

normalized
  strategy := self strategy.
  normalizingSum := strategy sum.
  ^strategy collect: [:each |
    normalizingSum strictlyPositive
      ifTrue: [each / normalizingSum]
      ifFalse: [1.0 / Action subclasses size]]

, который, кстати, можно переписать как

normalized
  | strategy normalizingSum |
  strategy := self strategy.
  normalizingSum := strategy sum.
  ^normalizingSum strictlyPositive
     ifTrue: [strategy collect: [:each | each / normalizingSum]]
     ifFalse: [strategy class new: sumstrategy size withAll: 1.0 / Action subclasses size]

Если у вас есть другие стратегии, кроме #max: 0, вы можете легко настроить кодвыше, чтобы сделать его более общим, используя perform: или имея специальный подкласс StrategyCollection, который реализует свою собственную версию #strategy.

Я также предложил бы добавить метод для выражения Action subclasses size,Что-то в строках

actionCount
  ^Action subclasses size

, а затем используйте это в #normalized.Выражение Action subclasses size хрупкое и может появляться в других методах.Например, если завтра вы решите сгруппировать некоторые подклассы Action в другой абстрактный подкласс, число подклассов Action не будет адаптировано к такому рефакторингу.В более общем смысле поведение ваших объектов не должно зависеть от того, как вы организовываете свой код, потому что это относится к метауровню абстракции.

0 голосов
/ 03 марта 2019

Вы должны определенно сделать его экземпляром Array или даже лучше Collection, поскольку ваш код работает на всем, что может быть повторено (и имеет числа).

Причина в том, что он более понятениспользовать:

#(3 5 49 3 1) normalized

вместо:

SomeMisteriousThirdParty normalize: #(3 5 49 3 1)

Кроме того, если у вас есть некоторые специальные коллекции или другие классы, они могут определить свои собственные версии normalized для правильной обработки.

Философия Pharo заключается в том, что никто не может создать идеальную среду именно для вашего проекта.Таким образом, легко изменить уже имеющиеся библиотеки в соответствии с вашими потребностями.

Способ сделать это - использовать методы расширения, в которых ваш пакет расширяет некоторый другой классс методом.Эта функциональность присутствует в Pharo (и Smalltalk в целом) уже более десяти лет, именно по этой причине, когда вам нужно расширить другой класс, но изменить версию вместе с вашим кодом.

Пока мы в темеЧто касается нормализации, то стоит отметить, что есть как минимум два крупных проекта, которые, скорее всего, нуждаются в нормализации, чтобы делать то, что они делают.Один из них - Roassal, используемый для визуализации данных, а другой - Polymath, используемый для различных вычислений.Вам может быть полезно взглянуть на то, что они делают.

...