Javascript полиморфизм без классов ООП - PullRequest
0 голосов
/ 13 ноября 2018

В языке JS или ООП полиморфизм создается различными типами.

Например:

class Field {...}

class DropdownField extends Field {
  getValue() { 
     //implementation ....
  }
}

Представьте, что у меня есть библиотека forms.js с некоторыми методами:

class Forms {
    getFieldsValues() {
      let values = [];
      for (let f of this.fields) {
          values.push(f.getValue());
      }
      return values;
    }
}

Получает все значения полей.Обратите внимание, что библиотеке все равно, какое это поле.

Таким образом, разработчик A создал библиотеку, а разработчик B может создавать новые поля: AutocompleterField.

Он может добавлять методы в AutocompleterField без изменения кода библиотеки.(Forms.js).

Если я использую метод функционального программирования в JS, как мне этого добиться?

Если у меня нет методов в объекте, я могу использовать операторы case, но это нарушает принцип,Аналогично этому:

if (field.type == 'DropdownField')...
else if (field.type == 'Autocompleter')..

Если разработчик B добавляет новый тип, он должен изменить код библиотеки.

Поэтому есть ли хороший способ решить проблему в JavaScript без использования объектно-ориентированного программирования.

Я знаю, что Js не совсем ООП или FP, но в любом случае.

Спасибо

Ответы [ 4 ]

0 голосов
/ 13 ноября 2018

JavaScript является многоцелевым языком, вы, конечно, можете решить его по-разному.При переключении на функциональное программирование ответ очень прост: используйте функции!Проблема с вашим примером заключается в следующем: он настолько урезан, что вы можете сделать то же самое, что и всего лишь с 3 строками:

// getValue :: DOMNode -> String
const getValue = field => field.value;

// readForm :: Array DOMNode -> Array String
const readForm = formFields => formFields.map(getValue);

readForm(Array.from(document.querySelectorAll('input, textarea, select')));
// -> ['Value1', 'Value2', ... 'ValueN']

Критическая вещь: как реализуется Field::getValue(), что делаетэто возврат ?Или точнее: чем DropdownField::getValue() отличается от AutocompleteField::getValue() и, например, NumberField::getValue()?Все ли они просто возвращают значение?Они возвращают пару имени и значения?Они даже нуждаются в , чтобы отличаться?

Вопрос заключается в том, отличаются ли ваши Field классы и их наследующие классы из-за того, как работают их методы getValue(), или они скорееотличаются из-за другой функциональности у них есть?Например, функциональность «автозаполнения» текстового поля не связана (или не должна) быть привязана к тому, как из него берется значение.

Если вам действительно нужно прочитать значения по-другому, выможет реализовать функцию, которая принимает карту / словарь / объект / POJO из {fieldtype: readerFunction} пар:

/* Library code */

// getTextInputValue :: DOMNode -> String
const getTextInputValue = field => field.value;

// getDropdownValue :: DOMNode -> String
const getDropdownValue = field => field.options[field.selectedIndex].value;

// getTextareaValue :: DOMNode -> String
const getTextareaValue = field => field.textContent;

// readFieldsBy :: {String :: (a -> String)} -> DOMNode -> Array String
readFieldsBy = kv => form => Object.keys(kv).reduce((acc, k) => {
  return acc.concat(Array.from(form.querySelectorAll(k)).map(kv[k]));
}, []);



/* Code the library consumer writes */

const readMyForm = readFieldsBy({
  'input[type="text"]': getTextInputValue,
  'select': getDropdownValue,
  'textarea': getTextareaValue
});

readMyForm(document.querySelector('#myform'));
// -> ['Value1', 'Value2', ... 'ValueN']

Примечание: я намеренно не упомянул такие вещи, как монада ввода / вывода, потому что это сделало бы вещи болеесложный, но вы можете посмотреть его.

0 голосов
/ 13 ноября 2018

Вы можете использовать фабричный шаблон, чтобы убедиться, что вы следуете принципу открытого закрытия. Этот принцип гласит: «Программные объекты (классы, модули, функции и т. Д.) Должны быть открыты для расширения, но закрыты для модификации».

class FieldValueProviderFactory {
    getFieldValue(field) {
        return this.providers.find(p => p.type === field.type).provider(field);
    }
    registerProvider(type, provider) {
        if(!this.providers) {
            this.providers = [];
        }

        this.providers.push({type:type, provider:provider});
    }
}

var provider = new FieldValueProviderFactory();
provider.registerProvider('DropdownField', (field) => [ 1, 2, 3 ]);
provider.registerProvider('Autocompleter', (field) => [ 3, 2, 1 ]);

class FieldCollection {
    getFieldsValues() {
        this.fields = [ { type:'DropdownField',value:'1' }, { type:'Autocompleter',value:'2' } ];

        let values = [];
        for (let field of this.fields) {
            values.push(provider.getFieldValue(field));
        }
        return values;
    }
}

Теперь, когда вы хотите зарегистрировать новые типы полей, вы можете зарегистрировать провайдера для них на заводе, и вам не нужно изменять код вашего поля.

new Field().getFieldsValues();
0 голосов
/ 13 ноября 2018

Зависит от того, что вы подразумеваете под "полиморфизмом". Существует так называемый специальный полиморфизм , который предоставляют классы типов в Haskell, Scala или PureScript - и этот тип диспетчеризации обычно реализуется путем передачи объектов-свидетелей вместе в качестве дополнительной функции. аргументы, которые затем будут знать, как выполнять полиморфную функциональность.

Например, следующий код PureScript (из документов ), который обеспечивает функцию show для некоторых типов:

class Show a where
  show :: a -> String

instance showString :: Show String where
  show s = s

instance showBoolean :: Show Boolean where
  show true = "true"
  show false = "false"

instance showArray :: (Show a) => Show (Array a) where
  show xs = "[" <> joinWith ", " (map show xs) <> "]"

example = show [true, false]

Он компилируется в следующий JS (который я сократил):

var Show = function (show) {
    this.show = show;
};

var show = function (dict) {
    return dict.show;
};

var showString = new Show(function (s) {
    return s;
});

var showBoolean = new Show(function (v) {
    if (v) {
        return "true";
    };
    if (!v) {
        return "false";
    };
    throw new Error("Failed pattern match at Main line 12, column 1 - line 12, column 37: " + [ v.constructor.name ]);
});

var showArray = function (dictShow) {
    return new Show(function (xs) {
        return "[" + (Data_String.joinWith(", ")(Data_Functor.map(Data_Functor.functorArray)(show(dictShow))(xs)) + "]");
    });
};

var example = show(showArray(showBoolean))([ true, false ]);

Здесь нет абсолютно никакой магии, только некоторые дополнительные аргументы. А на «вершине», где вы на самом деле знаете конкретные типы, вы должны передать соответствующие конкретные объекты-свидетели.

В вашем случае вы бы передавали что-то вроде HasValue свидетеля для разных форм.

0 голосов
/ 13 ноября 2018

В языке JS или OOP полиморфизм создается различными типами.

Да.Или, скорее, путем реализации интерфейса типа * в различных объектах.

Как я могу использовать полиморфизм Javascript без классов ООП

Вы, похоже, путать class es с типами здесь.Вам вообще не нужен синтаксис JS class для создания объектов.

Вы можете просто иметь

const autocompleteField = {
    getValue() {
        …
    }
};
const dropdownField = {
    getValue() {
        …
    }
};

и использовать два в своем экземпляре Forms.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...