在一些不基于传统OOP模型的编程语言中,由于没有或者不强调类与对象的关系,实现多态的方法并不能够通过类间的继承或者接口来实现。这种情况下也许函式编程和元编程的思想能给我们一点启发,本文用了没有类也没有接口的JavaScript作为用例来表述。示例代码中只包含了最基本的逻辑,不包含太多的容错处理,代码在Node.js下运行通过。
最大值最小值
我们可以先从一个最简单的问题开始着手:查找一堆数字中的最大值和最小值。这里假设把一堆整数放到数组中并用擂台算法的实现,其实查找最大值和最小值两个动作的其它部份逻辑都是相同的,唯一不同的就是比较两个值大小的逻辑。在找最大值的时候,我们要判断新来的值是否比较大;在找最小值时则相反。
先看代码:
var numbers = [2,3,1,5,4];
var maximum = function(max, current) {
return max > current ? max : current;
};
var minimum = function(min, current) {
return min < current ? min : current;
};
console.log(numbers.reduce(maximum));
console.log(numbers.reduce(minimum));
console.log(numbers);
以上代码输出结果:
5
1
[ 2, 3, 1, 5, 4 ]
我们定义了maxinum和mininum两个函数,它们都接受两个值:目前为止的最大值,和一个还没进行比较的新值。然后执行对比的逻辑,并返回更大或者更小的一个。
我们用到了ES5标准中的Array.prototype.reduce函数,对整数组进行递减运算,并把用于计算最大值或最小值的函数作为参数传到reduce中(类似于事件编程中的回调函数)。在reduce开始执行时,它会把上一次reduce的值传进去作为函数的第一个参数,再把当前正在处理的值传为第二个参数。由于每一次reduce执行时,都是设用maximum或者mininum来得到一个结果,所以实际上,我们的操作是把每一轮选出来比较大的数,和数组中下一个数进行比较,以此类推。
关于reduce的详情,可参见:https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/Reduce
排序
又如排序问题也基本相同,升序和降序的逻辑也绝大部份相同,只是比较两个数大小的那部份不一样。
先看代码:
var numbers = [2,3,1,5,4];
var ascendant = function(left, right) {
if (left == right) return 0;
return left > right ? 1 : -1;
};
var descendant = function(left, right) {
if (left == right) return 0;
return left < right ? 1 : -1;
};
console.log(numbers.concat().sort(ascendant));
console.log(numbers.concat().sort(descendant));
console.log(numbers);
以上代码的输出结果:
[ 1, 2, 3, 4, 5 ]
[ 5, 4, 3, 2, 1 ]
[ 2, 3, 1, 5, 4 ]
在代码中定义了ascendant和descendant两个函数,它们都可接受两个参数,并跟据传入的两个数的大小情况,返回相应的0、-1或者1。这两个函数目前只处理数字类型,我们会在下一节描述如何处理复合数据。
在ES3中已经定义了一个Array.prototype.sort函数用于排序数组,该函数接受另一个对比函数作为它的第一个参数,并且会把邻近的两个值传到这个对比函数中,由这个对比函数执行比较两个数大小的逻辑,最后跟据返回值是否等于0、大于0或小于0三种情况来决定如何排序数组。
在调用sort之前还执行了一次concat,这是一个用于复制数组却又想偷懒时用的小技巧。因为sort会改变调用它的数组的顺序,所以要复制一份以保证原数组不被修改。这也是函式编程的思想之一,避免外部状态和可变数组对函数返回结果所造成的影响。
关于sort的详情,可参见:https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/sort
复合数据类型排序
在实现应用中,经常会遇到一个由复合数据类型组成的数组,比如说对象数组。这时可能需要根据每个对象中的某个值进行排序。如以下代码中有若干玩家players对象,要对攻击力offensive进行升序排列,以及对防御力defensive进行降序排列。
代码如下:
var players = [
{offensive: 2, defensive: 5},
{offensive: 1, defensive: 1},
{offensive: 3, defensive: 3},
{offensive: 5, defensive: 2},
{offensive: 4, defensive: 4},
];
var sortBy = function(fieldName, orderDirection) {
var field = fieldName;
var order = orderDirection < 0 ? -1 : 1;
return function(left, right) {
if (left[field] == right[field]) return 0;
return (left[field] > right[field] ? 1 : -1) * order;
};
};
console.log(players.concat().sort(sortBy("offensive", 1)));
console.log(players.concat().sort(sortBy("defensive", -1)));
console.log(players);
以上代码输出结果:
[ { offensive: 1, defensive: 1 },
{ offensive: 2, defensive: 5 },
{ offensive: 3, defensive: 3 },
{ offensive: 4, defensive: 4 },
{ offensive: 5, defensive: 2 } ]
[ { offensive: 2, defensive: 5 },
{ offensive: 4, defensive: 4 },
{ offensive: 3, defensive: 3 },
{ offensive: 5, defensive: 2 },
{ offensive: 1, defensive: 1 } ]
[ { offensive: 2, defensive: 5 },
{ offensive: 1, defensive: 1 },
{ offensive: 3, defensive: 3 },
{ offensive: 5, defensive: 2 },
{ offensive: 4, defensive: 4 } ]
sortBy这个函数可以跟据传入的两个参数:用于排序的属性,以及升序1还是降序-1,来动态生成一个提供给sort函数的用的比较两个对象的函数。
这个示例中同时用了函式编程和元编程的思想:把函数作为数据,在运行期动态生成和传递。这样利用了动态脚本的灵活性,达到了实现多态的目的,同时也不需要使用传统OOP中过于精致和复杂的类和接口的设计。
欢迎各位指点和交流 @liuming
分享到:
相关推荐
### 面向对象编程与C++中的继承和多态 #### 1. 面向对象编程思想的诞生 面向对象编程(Object-Oriented Programming, OOP)的引入,是为了克服传统面向过程编程的一些局限性。面向过程编程,如C语言,更侧重于按...
多态的实现主要体现在以下几个方面: 1. **继承**:Java中的类可以继承另一个类,创建出一个新的类。继承关系为多态提供了基础,只有存在继承关系,子类才能共享父类的属性和方法,并可能对这些进行扩展或重写。 2...
创建多态VI在LabVIEW中的实现主要依赖于函数选板(Function Palette)上的数据类型结构(Data Type Structure)。下面将详细介绍如何在LabVIEW中创建和使用多态VI。 1. **定义多态VI的基本结构** 在LabVIEW中,一...
Java 编译时多态和运行时多态 Java 编译时多态和运行时多态是 Java 语言中两个重要的概念,它们都是多态性的实现方式,但它们在实现机制和应用场景上有所不同。 编译时多态 编译时多态是指在编译期根据参数列表的...
接着,我们来看多态的两种主要形式:编译时多态和运行时多态。编译时多态通常通过函数重载和运算符重载实现,编译器在编译阶段就能确定调用哪个函数。而运行时多态则是在程序运行时才决定调用哪个函数,这是通过虚...
在编程领域,多态(Polymorphism)是一个关键概念,尤其在面向对象编程中发挥着重要作用。多态允许我们使用一个通用的接口来处理不同类型的对象,提供了代码的灵活性和可扩展性。虚函数(Virtual Function)是实现...
多态分为编译时多态和运行时多态。编译时多态主要通过方法的重载(Overloading)实现,即在同一个类中定义多个同名但参数列表不同的方法。而运行时多态则是通过方法的重写(Overriding)和接口实现实现,它依赖于...
Java多态的讲解
### Java多态详解 #### 什么是多态? 多态(Polymorphism)是面向对象编程的一个核心特性,指的是一个接口可以被多个类所实现或一个类可以被多个类所继承,进而表现出不同的行为。在Java中,多态主要体现在两个...
在编程领域,多态(Polymorphism)是面向对象编程中的一个重要概念,它允许我们用一个接口处理多种不同的数据类型。多态性使得程序更加灵活,可扩展性更强,能够提高代码的重用率。本练习案例将深入探讨多态的常见...
在Java中,多态分为编译时多态和运行时多态。编译时多态主要体现在方法的重载(Overloading),即在同一个类中可以有多个同名方法,但参数列表不同。编译器会根据传入参数的类型和数量来决定调用哪个方法。运行时...
这主要体现在两个方面:静态多态(编译时多态)和动态多态(运行时多态)。静态多态通常通过函数重载或运算符重载实现,而动态多态则依赖于接口、继承和虚函数等机制。 1. 静态多态:在编译期间就能确定调用哪个...
java多态实现的课件,给那些需要的人 讲述了java多态的概念、分类、实现 综合案例讲解、总结多态的作用
在C++中,多态(Polymorphism)是面向对象编程的一个核心概念,它允许我们使用一个接口来表示多种不同的类型。多态性使得代码更加灵活,可扩展性更强,可以减少代码重复,并提高代码的复用性。在这个文档中,通过两...
封装继承多态总结
### Java多态经典讲解 #### 一、理解Java多态 多态,作为面向对象编程的三大特性之一,是继抽象和继承之后的关键概念。它允许我们以统一的接口处理不同类型的对象,增强了代码的灵活性和可扩展性。在Java中,多态...
cpp 多态
在IT行业中,多态(Polymorphism)是一个核心概念,主要在面向对象编程(Object-Oriented Programming, OOP)中出现。它允许我们使用一个接口来表示多种不同的类型,从而提高了代码的灵活性、可扩展性和可重用性。在...
"Animal(多态练习)"这个主题,显然是为了帮助开发者理解并掌握多态这一面向对象的核心概念,同时结合设计模式的基础应用。 多态(Polymorphism)是面向对象三大特性(封装、继承、多态)之一,它允许我们使用一个...
国外一款多态引擎asm 作为代码变形的一项技术