javascript设计模式之鸭子类型和多态

目录
  • 1.鸭子类型
  • 2.多态
    • 2.1 java多态
    • 2.2 js多态
  • 总结

本文参考曾探编写的JavaScript设计模式与开发实践

设计模式的实现都遵循一条原则,即“找出程序中变化的地方,并将变化封装起来”。一个程序的设计总是可以分为可变的部分和不变的部分。当我们找出可变的部分,并且把这些部分封装起来,那么剩下的就是不变和稳定的部分。这些不变和稳定的部分是非常容易复用的。这也是设计模式为什么描写的是可复用面向对象软件基础的原因。

1.鸭子类型

鸭子类型的通俗说法是:“如果它走起路来像鸭子,叫起来也是鸭子,那么它就是鸭子。”

鸭子类型专业解释:例如,在不使用鸭子类型的语言中,我们可以编写一个函数,它接受一个类型为"鸭子"的对象,并调用它的"走"和"叫"方法,但它只能接受鸭子类型的对象,否则报错。在使用鸭子类型的语言中,这样的一个函数可以接受一个任意类型的对象,并调用它的"走"和"叫"方法。如果这些需要被调用的方法不存在,那么将引发一个运行时错误。任何拥有这样的正确的"走"和"叫"方法的对象都可被函数调用这种行为就是符合鸭子类型。

所以如果弱类型语言(js)函数需要接收参数,为保证健壮性,则应先判断参数类型,并判断参数是否包含需要访问的方法、属性。只有当这些条件满足时,程序才真正处理调用参数的方法、参数

    var duck = {
            sing: function() {
                console.log('嘎嘎嘎');
            }
        }
        var chicken = {
            sing: function() {
                console.log('嘎嘎嘎');
            }
        }
        var choir = [] //合唱团
        function joinChoir(duck) {
            if (duck && typeof duck.sing === 'function') {
                choir.push(duck)
                console.log('合唱队添加了一个成员');
            }
        }
        joinChoir(duck)
        joinChoir(chicken)
            // 大合唱
        for (let i = 0; i < choir.length; i++) {
            choir[i].sing()
        }

2.多态

2.1 java多态

对面向对象来说,多态分为编译时多态和运行时多态。其中编译时多态是静态的,主要是指方法的重载,它是根据参数列表的不同来区分不同的方法。通过编译之后会变成两个不同的方法,在运行时谈不上多态。而运行时多态是动态的,它是通过动态绑定来实现的,也就是大家通常所说的多态性。

在java里,多态是同一个行为具有不同表现形式或形态的能力,即对象多种表现形式的体现,就是指程序中定义的引用变量所指向的具体类型和通过该引用变量发出的方法调用在编程时并不确定,而是在程序运行期间才确定,即一个引用变量倒底会指向哪个类的实例对象,该引用变量发出的方法调用到底是哪个类中实现的方法,必须在由程序运行期间才能决定。在简单来说,编译时对象是父类类型,到真正运行时,对象才可以知道具体是哪个子类类型,才知道调用哪个子类中实现的方法

Java 实现多态有 3 个必要条件:继承、重写和向上转型。只有满足这 3 个条件,开发人员才能够在同一个继承结构中使用统一的逻辑实现代码处理不同的对象,从而执行不同的行为。

  • 继承:在多态中必须存在有继承关系的子类和父类。
  • 重写:子类对父类中某些方法进行重新定义,在调用这些方法时就会调用子类的方法。
  • 向上转型:在多态中需要将子类的引用赋给父类对象,只有这样该引用才既能可以调用父类的方法,又能调用子类的方法。

由此可以得出使用多态的好处,我们可以很好的完成代码的解耦和工作,加强代码的可扩展性,是代码更加灵活,在不改变原有接口方法的情况下简化流程等,总结一下就是:

  • 减耦合
  • 增强可以替换性
  • 可扩展性
  • 灵活性等…

举个生活例子,如下图所示:使用手机扫描二维码支付时,二维码并不知道客户是通过何种方式进行支付,只有通过二维码后才能判断是走哪种支付方式执行对应流程。

举个代码例子,创建 Figure 类,在该类中首先定义存储二维对象的尺寸,然后定义有两个参数的构造方法,最后添加 area() 方法,该方法计算对象的面积。代码如下:

public class Figure {
    double dim1;
    double dim2;
    Figure(double d1, double d2) {
        // 有参的构造方法
        this.dim1 = d1;
        this.dim2 = d2;
    }
    double area() {
        // 用于计算对象的面积
        System.out.println("父类中计算对象面积的方法,没有实际意义,需要在子类中重写。");
        return 0;
    }
}

创建继承自 Figure 类的 Rectangle 子类,该类调用父类的构造方法,并且重写父类中的 area() 方法。代码如下:

public class Figure {
    double dim1;
    double dim2;
    Figure(double d1, double d2) {
        // 有参的构造方法
        this.dim1 = d1;
        this.dim2 = d2;
    }
    double area() {
        // 用于计算对象的面积
        System.out.println("父类中计算对象面积的方法,没有实际意义,需要在子类中重写。");
        return 0;
    }
}

创建继承自 Figure 类的 Triangle 子类,该类与 Rectangle 相似。代码如下:

public class Rectangle extends Figure {
    Rectangle(double d1, double d2) {
        super(d1, d2);
    }
    double area() {
        System.out.println("长方形的面积:");
        return super.dim1 * super.dim2;
    }
}

创建 Test 测试类,在该类的 main() 方法中首先声明 Figure 类的变量 figure,然后分别为 figure 变量指定不同的对象,并调用这些对象的 area() 方法。代码如下:

public class Rectangle extends Figure {
    Rectangle(double d1, double d2) {
        super(d1, d2);
    }
    double area() {
        System.out.println("长方形的面积:");
        return super.dim1 * super.dim2;
    }
}

从上述代码可以发现,无论 figure 变量的对象是 Rectangle 还是 Triangle,它们都是 Figure 类的子类,因此可以向上转型为该类,从而实现多态。

2.2 js多态

多态的实际含义:是同一操作作用于不同的对象上面,可以产生不同的解释和不同的执行结果。换句话说,给不同的对象发送同一个消息的时候,这些对象会根据这个消息分别给出不同的反馈。

来举例说明一下多态的实际含义:

主人家里养了两只动物,分别是一只鸭和一只鸡,当主人向它们发出“叫”的命令时,鸭会“嘎嘎嘎”地叫,而鸡会“咯咯咯”地叫。这两只动物都会以自己的方式来发出叫声。它们同样“都是动物,并且可以发出叫声”,但根据主人的指令,它们会各自发出不同的叫声。

      var Duck = function() {
        }
        Duck.prototype.sing = function() {
            console.log('嘎嘎嘎');
        }
        var Chicken = function() {
        }
        Chicken.prototype.sing = function() {
            console.log('嘎嘎嘎');
        }
        function sing(animal) {
            if (animal && (typeof animal.sing === 'function')) {
                animal.sing()
            }
        }
        sing(new Duck())
        sing(new Chicken())

多态背后的思想:是将“做什么”和“谁去做以及怎样去做”分离开来,也就是将“不变的事物”与 “可能改变的事物”分离开来。在这个故事中,动物都会叫,这是不变的,但是不同类型的动物具体怎么叫是可变的。把不变的部分隔离出来,把可变的部分封装起来,这给予了我们扩展程序的能力,程序看起来是可生长的,也是符合开放—封闭原则的,相对于修改代码来说,仅仅增加代码就能完成同样的功能,这显然优雅和安全得多。

多态的实现:多态的思想实际上是把“做什么”和“谁去做”分离开来,要实现这一点,归根结底先要消除类型之间的耦合关系。如果类型之间的耦合关系没有被消除,那么我们在makeSound 方法中指定了发出叫声的对象是某个类型,它就不可能再被替换为另外一个类型。在 Java 中,可以通过向上转型来实现多态。而 JavaScript 的变量类型在运行期是可变的。一个JavaScript 对象,既可以表示 Duck 类型的对象,又可以表示 Chicken 类型的对象,这意味着 JavaScript 对象的多态性是与生俱来的。这种与生俱来的多态性并不难解释。JavaScript 作为一门动态类型语言,它在编译时没有类型检查的过程,既没有检查创建的对象类型,又没有检查传递的参数类型。

多态的最根本好处:你不必再向对象询问“你是什么类型”而后根据得到的答案调用对象的某个行为——你只管调用该行为就是了,其他的一切多态机制都会为你安排妥当。换句话说,多态最根本的作用就是通过把过程化的条件分支语句转化为对象的多态性,从而消除这些条件分支语句

多态的最根本好处,可以用下面这个例子很好地诠释:在电影的拍摄现场,当导演喊出“action”时,主角开始背台词,照明师负责打灯光,后面的群众演员假装中枪倒地,道具师往镜头里撒上雪花。在得到同一个消息时,每个对象都知道自己应该做什么。如果不利用对象的多态性,而是用面向过程的方式来编写这一段代码,那么相当于在电影开始拍摄之后,导演每次都要走到每个人的面前,确认它们的职业分工(类型),然后告诉他们要做什么。如果映射到程序中,那么程序中将充斥着条件分支语句。利用对象的多态性,导演在发布消息时,就不必考虑各个对象接到消息后应该做什么。对象应该做什么并不是临时决定的,而是已经事先约定和排练完毕的。每个对象应该做什么,已经成为了该对象的一个方法,被安装在对象的内部,每个对象负责它们自己的行为。所以这些对象可以根据同一个消息,有条不紊地分别进行各自的工作。

将行为分布在各个对象中,并让这些对象各自负责自己的行为,这正是面向对象设计的优点。

总结

本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注我们的更多内容!

时间: 2022-01-13

详细介绍Python的鸭子类型

鸭子类型基本定义 首先Python不支持多态,也不用支持多态,python是一种多态语言,崇尚鸭子类型. 以下是维基百科中对鸭子类型得论述: 在程序设计中,鸭子类型(英语:duck typing)是动态类型的一种风格.在这种风格中,一个对象有效的语义,不是由继承自特定的类或实现特定的接口,而是由当前方法和属性的集合决定.这个概念的名字来源于由James Whitcomb Riley提出的鸭子测试,"鸭子测试"可以这样表述: "当看到一只鸟走起来像鸭子.游泳起来像鸭子.叫起来也

JS中的多态实例详解

多态在面向对象编程语言中是十分重要的.在JAVA中是通过继承来得到多态的效果.如下: public abstract class Animal { abstract void makeSound(); // 抽象方法 } public class Chicken extends Animal{ public void makeSound(){ System.out.println( "咯咯咯" ); } } public class Duck extends Animal{ publi

学习JavaScript设计模式(多态)

多态的实际含义是:同一操作作用于不同的对象上面,可以产生不同的解释和不同的执行结果.换句话说,给不同的对象发送同一个消息的时候,这些对象会根据这个消息分别给出不同的反馈. 从字面上来理解多态不太容易,下面我们来举例说明一下. 主人家里养了两只动物,分别是一只鸭和一只鸡,当主人向它们发出"叫"的命令时,鸭会"嘎嘎嘎"地叫,而鸡会"咯咯咯"地叫.这两只动物都会以自己的方式来发出叫声.它们同样"都是动物,并且可以发出叫声",但根据主

Python动态语言与鸭子类型详解

今天来说说编程语言中的动态类型语言与鸭子类型. 动态语言 维基百科对动态语言的定义: 动态编程语言是一类在运行时可以改变其结构的语言:例如新的函数.对象.甚至代码可以被引进,已有的函数可以被删除或是其他结构上的变化.动态语言目前非常具有活力如PHP.Ruby.Python 都属于动态语言,而C.C++.Java等语言则不属于动态语言. 这个解释很抽象,其实动态语言是相对静态语言而言的,静态语言的特点是在程序执行前,代码编译时从代码中就可以知道一切,比如变量的类型,方法的返回值类型: String

JavaScript多态与封装实例分析

本文实例讲述了JavaScript多态与封装.分享给大家供大家参考,具体如下: 1.静态语言类型和动态语言类型 编程语言按照数据类型可以分为静态语言类型和动态语言类型两大类. 1) 静态语言类型在编译时就已经确定变量的类型 优点:在编译时就能发现类型不匹配的错误,编译器可以帮助我们提前避免程序在运行期间可能发生的一些错误:在程序中明确规定了数据类型,编译器可以针对这些信息对程序进行优化工作. 缺点:迫使程序员依照契约来编写程序,为每个变量规定数据类型:类型的声明也会增加更多的代码,使得程序员难以

python 多态 协议 鸭子类型详解

接口(python 中的协议)的多种不同的实现方式即为多态.多态的作用,就是为了类在继承和派生的时候,保证使用"家谱"中任一类的实例的某一属性时的正确调用. from abc import ABCMeta, abstractmethod # 鸭子类 class Dock(metaclass=ABCMeta): @abstractmethod def Swimming(self): # 游泳方法协议(接口) pass @abstractmethod # 走路协议(接口) def Walk

对python 自定义协议的方法详解

前面说到最近在写python的一些东西,然后和另外一位小伙伴定义了协议,然后昨天我有一部分东西没理解对,昨天上午我自己重写了一遍接收和发送的全部逻辑,昨天下午补了压力测试的脚本,自测没问题之后告知联调的小伙伴. 结果上午还是出了一点问题,然后我们两对代码,他写了一个python的实现.还好最后我这边没问题.(我也害怕是我这边出问题啊,所以我自己的代码都自己检查了好几遍) 简单放一下他的实现: import struct import ctypes class E(Exception): def

Python的语言类型(详解)

Python 是强类型的动态脚本语言 . 强类型:不允许不同类型相加 动态:不使用显示数据类型声明,且确定一个变量的类型是在第一次给它赋值的时候 脚本语言:一般也是解释型语言,运行代码只需要一个解释器,不需要编译 强类型语言和弱类型语言 1.强类型语言:使之强制数据类型定义的语言.没有强制类型转化前,不允许两种不同类型的变量相互操作.强类型定义语言是类型安全的语言,如Java.C# 和 python,比如Java中"int i = 0.0;"是无法通过编译的: 2.弱类型语言:数据类型

对Python中画图时候的线类型详解

在Python中用matplotlib画图的时候,为了区分曲线的类型,给曲线上面加一些标识或者颜色.以下是颜色和标识的汇总. 颜色(color 简写为 c): 蓝色: 'b' (blue) 绿色: 'g' (green) 红色: 'r' (red) 蓝绿色(墨绿色): 'c' (cyan) 红紫色(洋红): 'm' (magenta) 黄色: 'y' (yellow) 黑色: 'k' (black) 白色: 'w' (white) 灰度表示: e.g. 0.75 ([0,1]内任意浮点数) RG

Python 变量类型详解

变量存储在内存中的值.这就意味着在创建变量时会在内存中开辟一个空间. 基于变量的数据类型,解释器会分配指定内存,并决定什么数据可以被存储在内存中. 因此,变量可以指定不同的数据类型,这些变量可以存储整数,小数或字符. 变量赋值 Python 中的变量赋值不需要类型声明. 每个变量在内存中创建,都包括变量的标识,名称和数据这些信息. 每个变量在使用前都必须赋值,变量赋值以后该变量才会被创建. 等号(=)用来给变量赋值. 等号(=)运算符左边是一个变量名,等号(=)运算符右边是存储在变量中的值. 例

python ctypes库2_指定参数类型和返回类型详解

python函数的参数类型和返回类型默认为int. 如果需要传递一个float值给dll,那么需要指定参数的类型. 如果需要返回一个flaot值到python中,那么需要指定返回数据的类型. 数据类型参考python文档: https://docs.python.org/3.6/library/ctypes.html#fundamental-data-types import ctypes path = r'E:\01_Lab\VisualStudioLab\cpp_dll\cpp_dll\De

Python面向对象之继承代码详解

本文研究的主要是Python面向对象之继承的相关内容,具体如下. Python 继承 即一个派生类(derived class)继承基类(bass class)字段和方法.继承也允许把一个派生类的对象作为一个基类对象对待.例如,有这样一个设计,一个Cat类型的对象派生自Animal类,这是模拟"是一个(is-a)"关系(例如,Cat是一个Animal). 继承实现了代码的重用. 继承的基本语法: class 派生类名(基类名1 [, 基类名2....]): 基类名写在括号里,基本类是在

python魔法方法-自定义序列详解

自定义序列的相关魔法方法允许我们自己创建的类拥有序列的特性,让其使用起来就像 python 的内置序列(dict,tuple,list,string等). 如果要实现这个功能,就要遵循 python 的相关的协议.所谓的协议就是一些约定内容.例如,如果要将一个类要实现迭代,就必须实现两个魔法方法:__iter__.next(python3.x中为__new__).__iter__应该返回一个对象,这个对象必须实现 next 方法,通常返回的是 self 本身.而 next 方法必须在每次调用的时

python模块之re正则表达式详解

一.简单介绍 正则表达式是一种小型的.高度专业化的编程语言,并不是python中特有的,是许多编程语言中基础而又重要的一部分.在python中,主要通过re模块来实现. 正则表达式模式被编译成一系列的字节码,然后由用c编写的匹配引擎执行.那么正则表达式通常有哪些使用场景呢? 比如为想要匹配的相应字符串集指定规则: 该字符串集可以是包含e-mail地址.Internet地址.电话号码,或是根据需求自定义的一些字符串集: 当然也可以去判断一个字符串集是否符合我们定义的匹配规则: 找到字符串中匹配该规

python 垃圾收集机制的实例详解

 python 垃圾收集机制的实例详解 pythonn垃圾收集方面的内容如果要细讲还是挺多的,这里只是做一个大概的概括 Python最主要和绝大多数时候用的都是引用计数,每一个PyObject定义如下: #define PyObject_HEAD \ Py_ssize_t ob_refcnt; \ struct _typeobject *ob_type; typedef struct _object { PyObject_HEAD } PyObject; 每个pyobject都有一个refcnt