简体   繁体   English

没有OOP类的Java语言多态性

[英]Javascript polymorphism without OOP classes

In JS or OOP language the polymorhpism is created by making different types. 在JS或OOP语言中,通过创建不同的类型来创建多态性。

For example: 例如:

class Field {...}

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

Imagine I have library forms.js with some methods: 想象一下,我有一些方法的库Forms.js:

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

This gets all field values. 这将获取所有字段值。 Notice the library doesnt care what field it is. 注意,该库并不关心它是哪个字段。

This way developer A created the library and developer B can make new fields: AutocompleterField. 这样,开发人员A创建了库,而开发人员B可以创建新字段:AutocompleterField。

He can add methods in AutocompleterField withouth changing the library code (Forms.js) . 他可以在AutocompleterField中添加方法,而无需更改库代码(Forms.js)。

If I use functional programming method in JS, how can I achieve this? 如果我在JS中使用函数式编程方法,该如何实现?

If I dont have methods in object i can use case statements but this violates the principle. 如果我在对象中没有方法,则可以使用case语句,但这违反了原理。 Similar to this: 与此类似:

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

If developer B add new type he should change the library code. 如果开发人员B添加新类型,他应该更改库代码。

So is there any good way to solve the issue in javascript without using object oriented programming. 因此,有什么好的方法可以在不使用面向对象的编程的情况下解决javascript中的问题。

I know Js isnt exactly OOP nor FP but anyway. 我知道Js既不是OOP也不是FP,但是无论如何。

Thanks 谢谢

JavaScript being a multi-purpose language, you can of course solve it in different ways. JavaScript是一种多用途语言,您当然可以用不同的方式解决它。 When switching to functional programming, the answer is really simple: Use functions! 切换到函数式编程时,答案非常简单:使用函数! The problem with your example is this: It is so stripped down, you can do exactly the same it does with just 3 lines: 您的示例的问题是:如此简单,您只需执行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']

The critical thing is: How is Field::getValue() implemented, what does it return ? 关键是: Field::getValue()如何实现, 返回什么? Or more precisely: How does DropdownField::getValue() differ from AutocompleteField::getValue() and for example NumberField::getValue() ? 或更准确地说: DropdownField::getValue()AutocompleteField::getValue()以及例如NumberField::getValue()有何不同? Do all of them just return the value? 它们都只是返回值吗? Do they return a pair of name and value? 他们返回一对名称和值吗? Do they even need to be different? 他们甚至需要有所不同吗?

The question is therefor, do your Field classes and their inheriting classes differ because of the way their getValue() methods work or do they rather differ because of other functionality they have? 因此,问题是,您的Field类及其继承类是否因其getValue()方法的工作方式而有所不同,还是由于它们具有的其他功能而有所不同? For example, the "autocomplete" functionality of a textfield isn't (or shouldn't be) tied to the way the value is taken from it. 例如,文本字段的“自动完成”功能与(或不应被)绑定到从中获取值的方式。

In case you really need to read the values differently, you can implement a function which takes a map/dictionary/object/POJO of {fieldtype: readerFunction} pairs: 如果确实需要以不同的方式读取值,则可以实现一个函数,该函数采用{fieldtype: readerFunction}对的map / dictionary / object / POJO:

/* 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']

Note: I intentionally didn't mention things like the IO monad here, because it would make stuff more complicated, but you might want to look it up. 注意:我故意在这里没有提到IO monad之类的东西,因为它会使事情变得更复杂,但是您可能需要查找它。

In JS or OOP language the polymorhpism is created by making different types. 在JS或OOP语言中,通过创建不同的类型来创建多态性。

Yes. 是。 Or rather, by implementing the same type interface in different objects. 或者更确切地说,通过在不同对象中实现相同类型的接口。

How can I use Javascript polymorphism without OOP classes 没有OOP类的情况下如何使用Javascript多态性

You seem to confuse class es with types here. 您似乎在这里class与类型混为一谈 You don't need JS class syntax to create objects at all. 您根本不需要JS class语法来创建对象。

You can just have 你可以有

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

and use the two in your Forms instance. 并在您的Forms实例中使用两者。

Depends on what you mean by "polymorphism". 取决于您所说的“多态性”。 There's the so-called ad-hoc polymorphism which type classes in Haskell, Scala, or PureScript provide -- and this kind of dispatch is usually implemented by passing witness objects along as additional function arguments, which then will know how to perform the polymorphic functionality. 有一种所谓的即席多态性 ,它在Haskell,Scala或PureScript中提供类型类型的类,并且通常通过将见证对象作为附加函数参数传递来实现这种分派,然后,他们将知道如何执行多态性功能。 。

For example, the following PureScript code (from the docs ), which provides a show function for some types: 例如,以下PureScript代码(来自docs ),提供了某些类型的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]

It gets compiled to the following JS (which I shortened): 它被编译为以下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 ]);

There's absolutely no magic here, just some additional arguments. 这里绝对没有魔术,只有一些附加参数。 And at the "top", where you actually know concrete types, you have to pass in the matching concrete witness objects. 在实际知道具体类型的“顶部”,您必须传递匹配的具体见证对象。

In your case, you would pass around something like a HasValue witness for different forms. 在您的情况下,您将绕过类似HasValue见证程序的表单。

You could use a the factory pattern to ensure you follow the open close principle. 您可以使用工厂模式来确保您遵循打开关闭原则。 This principle says "Software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification". 该原则说“软件实体(类,模块,功能等)应为扩展而开放,而为修改而封闭”。

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;
    }
}

Now when you want to register new field types you can register a provider for them in the factory and don't have to modify your field code. 现在,当您要注册新的字段类型时,可以在工厂为其注册一个提供程序,而不必修改您的字段代码。

new Field().getFieldsValues();

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM