我想知道如何在函数式编程中创建多态。 让我们以具有不同类型输入的表格类为例 JS es6中的样本多态性:
class Form {
constructor(fields){
this.fields = fields;
}
validate() {
let err = null;
for(let f of this.fields) {
if (! field.validate()){
return field.getErr();
}
}
}
}
class TextField {
isNotEmpty() {
return this.value != ''
}
validate() {
return this.isNotEmpty()
}
}
class SelectField {
validate() {
if (! inArray(this.value, this.options) {
return 'value is not in the available options'
}
}
}
该代码仅用于演示目的,不要查看语法或正确的行为。
我想知道如何在FP中创建类似的多态性。 在Haskell中你可以使用模式匹配,但是如何在javascript中进行操作?
有人说我可以用if语句实现不同的功能:
if (field.type == 'text')...
else if (field.type == 'select')...
但这很糟糕,因为我应该在一个函数中编写所有逻辑。 如果我想添加新类型的字段(例如MultipleSelect),我应该更改公共函数内的代码。
javascript中有更好的选择吗?
答案 0 :(得分:0)
我从OP提供的示例代码中得到的唯一方法是将当前处理的表单字段映射到field
- 特定的验证和错误处理功能......一个工作示例代码然后看起来像......
/**
* form and field type matching configuration tables/maps
*/
const validationTable = {
isValidFieldType: {
text: isValidTextField,
select: isValidSelectField,
//multiselect: ...
__invalid__: returnInvalidFieldValue
},
validateForm: validateForm
};
const createErrorTable = {
//form: createFormError,
field: {
text: createTextFieldError,
select: createSelectFieldError,
//multiselect: ...
__invalid__: handleCreateErrorException
}
};
/**
* helping getter functionality
* - that either returns a valid field type that does
* match a key in `validationTable.isValidFieldType`
* - or that does return the '__invalid__' key as it's
* fallback value.
*/
function getValidationFieldType(field) {
const fieldType = (field && field.type);
return (
(typeof fieldType === 'string')
&& (fieldType in validationTable.isValidFieldType)
&& fieldType
) || '__invalid__';
}
/**
* main validation functionality
* - that either returns `true` for an entirely valid form validation process
* - or that does return a type specific/related validation error.
*/
function validateForm(type) {
const tableOfIsValidFieldType = validationTable.isValidFieldType;
let recentField;
return Array.from(type.fields).every(field => {
recentField = field;
return tableOfIsValidFieldType[getValidationFieldType(field)](field);
}) || createErrorTable.field[getValidationFieldType(recentField)](/*recentField*/);
}
/**
* validation and error handling specific helper functionality
*/
function isValidTextField(type) {
return (type.value !== '');
}
function isValidSelectField(type) {
return Array.from(type.options).includes(type.value);
}
function returnInvalidFieldValue() {
return false;
}
function createTextFieldError(/*recentField*/) {
return (new Error('an empty text field value is not allowed.'));
}
function createSelectFieldError(/*recentField*/) {
return (new Error('the field value does not match the options'));
}
function handleCreateErrorException(/*recentField*/) {
return (new Error('there is no matching validation and error handling for this field.'));
}
/**
* test
*/
const validForm = { fields: [{
type: 'text',
value: 'valid'
}, {
type: 'select',
value: 'valid',
options: ['valid', 'select', 'field']
}] };
const invalidFormWithNullType = { fields: [{
type: 'text',
value: 'valid'
}, {
type: null,
value: 'valid'
}, {
type: 'select',
value: 'valid',
options: ['valid', 'select', 'field']
}] };
const invalidFormWithMissingType = { fields: [{
value: 'valid'
}, {
type: 'select',
value: 'valid',
options: ['valid', 'select', 'field']
}] };
const invalidFormWithEmptyTextValue = { fields: [{
type: 'text',
value: ''
}, {
type: 'select',
value: 'valid',
options: ['valid', 'select', 'field']
}] };
const invalidFormWithNonMatchingSelectValue = { fields: [{
type: 'text',
value: 'valid'
}, {
type: 'select',
value: 'invalid',
options: ['valid', 'select', 'field']
}] };
console.log('validateForm(validForm) : ', validateForm(validForm));
console.log('validateForm(invalidFormWithNullType).message : ', validateForm(invalidFormWithNullType).message);
console.log('validateForm(invalidFormWithMissingType).message : ', validateForm(invalidFormWithMissingType).message);
console.log('validateForm(invalidFormWithEmptyTextValue).message : ', validateForm(invalidFormWithEmptyTextValue).message);
console.log('validateForm(invalidFormWithNonMatchingSelectValue).message : ', validateForm(invalidFormWithNonMatchingSelectValue).message);

.as-console-wrapper { max-height: 100%!important; top: 0; }

<强> 注意 强>
这几乎也符合他昨天在其中一条评论中提及的@Bergi。
请注意,OOP和FP是正交的,您可以轻松地将它们组合起来。
这里基于功能的解决方案满足将这些功能分配为其数据的两个对象。从现在开始的字段将被这些函数视为纯数据(如示例所示),但始终必须具有额外的type
属性。