关于oop:面向对象编程你真正懂吗

JavaScript 面向对象面向对象的两大编程思维面向过程 面向过程很好了解,就是剖析出解决问题所须要的步骤,而后用函数把这些步骤一步一步实现,应用的时候再一个一个的顺次调用就能够了 面向对象 上大学时老师教过咱们,万物皆对象。面向对象就是把事务合成成为一个个对象,而后由对象之间分工与单干。 举个例子 一个人盖个狗窝,间接弄泥,砖头,润饰一下就成了 然而盖高楼的话,须要打地基,须要运输资料,须要财务结算等,此时不须要等,各做各的,效率才高 面向对象三大个性就拿拖拉机来说 封装性【曾经把耕地性能筹备好,负责开就行】继承性【继承与拖拉机,会开拖拉机就会弄这个,继承自拖拉机】多态性【能够拆卸不同的机械,除了耕地还能够拉货】面向过程和面向对象的优缺点面向过程 长处:性能比面向对象高,步骤练习严密毛病:不好保护,不易屡次应用及扩大面向对象 长处:易保护,可复用,可扩大,灵活性高毛病性能没有面向过程高ES6中的类和对象类class在ES6中新减少了类的概念,能够应用class关键字申明一个类,之后以这个类来实例化对象。【构造函数实例化对象】 类形象了对象的公共局部,它泛指某一大类(class) ES5:没有类,ES6:类 ES:ECMAscript 类是在ES6中新加进入的,学会区分类和对象的概念 类:泛指一类 对象:类中的具体的某个实例,【属性和办法的集合体】 类:形象 类模仿形象的,泛指的,对象是具体的 面向对象模仿事实世界,更贴近理论生存,生活照分为形象事物和具体事物 比方:手机【两层含意:具体某个手机,和抽象的概念手机】 抽取,把对象的属性和行为封装成一个类 对类进行实例化, 获取类的对象 例如:人有身高,体重等,然而具体的某个人也有这个属性 练习理解类和对象 一个对象能够继承多个类,而类就是一个泛指 对象:具体 对象:类中的具体的某个实例【属性和办法的集合体】 现实生活中:万物皆对象,对象是一个具体的事物,看得见摸得着的实物。例如,一本书、一辆汽车、一个人能够是对象 在JavaScript 中,对象是一组无序的相干属性和办法的汇合,所有的事物都是对象,例如字符串、数值、数组、函数等。 var n = 3;var arr = [1,2,3];function fn () {}var str= 'abcd';<img src='a.jpg'>复制代码对象是由属性和办法组成的 属性:对象有什么【拜访】【语法:对象.属性】 办法:对象做什么【执行】【语法:对象.办法()】 属性:事物的特色,在对象中用属性来示意(罕用名词) 办法:事物的行为,在对象中用办法来示意(罕用动词) 面向对象的思维特点 抽取(形象)对象共用的属性和办法组织(封装)成一个类(模板) 对类进行实例化, 获取类的对象 类外面的属性和办法,其实是把对象的公共属性和公共办法放到了一起 创立类语法:class 类名 {属性和办法}【构造函数语法糖】 留神类名首字母大写 类要抽取公共属性办法,定义一个类 class Star {};var ldh = new Star();复制代码类就是构造函数的语法糖 constructor 构造函数语法 class Star { constructor (uname,age) { this.uname = uname; this.age = age; }}复制代码属性:放到constructor,构造函数外面 ...

April 7, 2022 · 2 min · jiezi

关于oop:扒一扒面向对象编程的另一面

摘要:只管有很多小伙伴对面向对象的概念曾经很很熟了,然而到底什么是面向对象编程?面向对象有哪些个性?面向对象编程可能为咱们带来哪些便当?面向对象又有哪些有余呢?本文分享自华为云社区《【云驻共创】被吹捧了很多年的面向对象编程真的就这么完满吗?》,作者: 冰 河 。 说到面向对象,置信很多小伙伴都曾经很相熟了,毕竟像Java、C++这种编程语言长期排在编程语言榜的前列,而且简直每一所大学的软件工程业余和计算机专业都会开设Java和C++相干的课程。在上学时,老师就会给咱们将什么是对象、什么类。比方:在Java中,万物皆对象。 说到这儿,只管有很多小伙伴对面向对象的概念曾经很很熟了,然而到底什么是面向对象编程?面向对象有哪些个性?面向对象编程可能为咱们带来哪些便当?面向对象又有哪些有余呢? 很少有人深究过这些问题,置信不少小伙伴都是都是只知其一;不知其二的,明天,咱们就一起来聊聊面向对象这个话题。 什么是面向对象编程?首先,咱们来聊聊什么是面向对象编程。面向对象编程的英文是Object Oriented Programming,简称OOP。在面向对象中有两个最重要,也是最根本的概念,那就是:类和对象。 这两个概念最早呈现到 Simula 编程语言中。而真正第一次应用面向对象编程这个概念的是在 Smalltalk 语言中。而 Smalltalk 语言也被公认为是世界上第一个真正意义上的面向对象编程语言。 倒退到明天,面向对象的编程语言越来越多,像C++、Java、Go、Python、C#、Object-C、Scala等都是面向对象编程语言。 其实,面向对象编程和面向对象编程语言是有着实质的区别的。 面向对象编程: 面向对象编程更多的是指一种编程束缚,以类和对象这两个概念作为输入的载体,并提供 封装、继承、形象、多态 四个个性。 面向对象编程语言: 面向对象编程语言实质上就是一种输入表白面向对象的工具,它可能将程序员的所思所想转换成计算机可能辨认的二进制码,最终可能在计算机上运行。 面向对象有哪些个性?后面咱们曾经简略的提及了面向对象的四大个性:封装、继承、形象和多态 。 封装封装,顾名思义,就是将要拜访的数据保护起来,不让外界间接拜访类的属性和字段,而是对外提供无限的拜访接口,受权内部仅能通过类提供的接口进行拜访。例如,咱们以Java语言为例,写一个只蕴含用户姓名userName和用户年龄userSex的User类。 public class User{ private String userName; private Integer userSex; public void setUserName(String userName){ this.userName = userName; } public String getUserName(){ return this.userName; } public void setUserSex(Integer userSex){ this.userSex = userSex; } public Integer getUserSex(){ return this.userSex; }}在这个User类中,总共有两个属性,也叫做类的成员变量,别离为userName和userSex。userName示意用户的姓名,userSex示意用户的名称。 咱们无奈间接拜访User类中的userName和userSex属性,因为它们都是被privaite修饰符润饰的。然而咱们能够通过如下四个办法访问者两个属性。 • void setUserName(String userName)• String getUserName()• setUserSex(Integer userSex)• Integer getUserSex() ...

September 29, 2021 · 2 min · jiezi

关于oop:聊聊IOC中依赖注入那些事-dependency-inject

What is Dependency injection依赖注入定义为组件之间依赖关系由容器在运行期决定,形象的说即由容器动静的将某个依赖关系注入到组件之中在面向对象编程中,咱们常常解决的问题就是解耦,管制反转(IoC)就是罕用的面向对象编程的设计准则,其中依赖注入是管制反转最罕用的实现。指标解决以后类不负责被依赖类实例的创立和初始化。 what is Dependency依赖是程序中常见的景象,假如有 A和B都被C耦合依赖着,在 OOP 编程中依赖无处不在。依赖模式有多种表现形式,比方一个类向另一个类发消息,一个类是另一个类的成员,一个类是另一个类的参数。 class A {}class B { classA: A; constructor() { this.classA = new A(); }}class C { classA: A; classB: B; constructor() { this.classA = new A(); this.classB = new B(); }}when is use Dependency injectioneg: 以用户调用 API 层打印日志来阐明 LoggerService被ApiService和UserService所依赖ApiService被UserService所依赖class LoggerService { constructor() { } log(args) { console.log(args) }}class ApiService { constructor ( private readonly logger: LoggerService ) { this.logger.log('api constructor') } public async getMydata () { return { name: 'mumiao', hobby: 'focusing in web'} }}class UserService { constructor ( private readonly api: ApiService, private readonly logger: LoggerService ) { this.logger.log('user constructor') } async getMyhobby () { const { hobby } = await this.api.getMydata() return hobby }}async function Main { const loggerService = new LoggerService() const apiService = new ApiService(loggerService) const userService = new UserService(loggerService, userService) console.log('my hobby is', await userService.getMyhobby())}Main()存在的问题Unit tests 很难写组件不易复用和保护,可扩展性比拟低UserService 不应该承载ApiService和LoggerService实例的创立。如何解决采纳依赖注入,UserService不负责被依赖类的创立和销毁,而是通过内部传入api和logger对象的形式注入。常见依赖注入形式有三种,本文次要以结构器注入为例解释。 ...

May 14, 2021 · 3 min · jiezi

关于抽象工厂模式:抽象工厂模式

定义提供一个创立一系列相干或相互依赖对象的接口,而无需指定它们具体的类。 模式概述形象工厂模式是所有模式的工厂模式中最为形象和最具一般性的一种模式,该模式与工厂办法模式的区别在于:工厂办法模式针对的是一个产品等级构造,而形象工厂模式须要面对多个产品等级构造,即形象工厂模式创立的并不是一个简略的对象,而是能够创立出多个位于不同产品等级、属于不同类型的具体产品对象。 产品等级构造和产品族产品等级构造:示意产品的继承构造,如一个抽象类是狗,子类包含哈士奇、牧羊犬等等,抽象类与具体的子类便形成了一个产品等级构造。 产品族:在形象工厂模式中,产品族是指由同一个工厂生产的产品,位于不同产品等级构造中的一组产品。如动物中的哈士奇和海东青,哈士奇属于狗的产品等级构造中,海东青属于老鹰的产品等级构造中,哈士奇和海东青便形成了一个产品族(动物)。 形象工厂模式的构造 AbstractFactory(形象工厂):申明了一组用于创立产品族的办法,其中每一个办法对应一个产品。Factory(具体工厂):继承自形象工厂,实现了形象工厂中申明的创立产品的办法。AbstractProduct(形象产品):为每中产品申明接口,具体产品的基类。Product(具体产品):具体产品对象,实现了形象产品中申明的业务办法。形象工厂模式的实现形象产品类abstract class AbstractProductA{ public void SameMethod() { //所有产品的公共办法 Console.WriteLine("所有产品A的公共办法"); } //差异化行为 public abstract void DiffMethod();}abstract class AbstractProductB{ public void SameMethod() { //所有产品的公共办法 Console.WriteLine("所有产品B的公共办法"); } //差异化行为 public abstract void DiffMethod();}具体产品类//产品A1class ProductA1 : AbstractProductA{ public override void DiffMethod() { //这里实现产品A1的业务逻辑 Console.WriteLine("这里实现产品A的业务逻辑"); }}//产品B1class ProductB1 : AbstractProductB{ public override void DiffMethod() { //这里实现产品B1的业务逻辑 Console.WriteLine("这里实现产品B的业务逻辑"); }}形象工厂类abstract class AbstractFactory{ public abstract AbstractProductA CreateProductA(); public abstract AbstractProductB CreateProductB();}具体工厂类class ConcreteFactory : AbstractFactory{ public override AbstractProductA CreateProductA() { return new ProductA1(); } public override AbstractProductB CreateProductB() { return new ProductB1(); }}应用var factory = new ConcreteFactory();var productA = factory.CreateProductA();productA.SameMethod();productA.DiffMethod();var productB = factory.CreateProductB();productB.SameMethod();productB.DiffMethod();后果开闭准则歪斜性如果在应用形象工厂模式的过程中思考不全面,在设计的时候脱漏了产品族中的某一产品,导致后续需要变动呈现新的产品时,须要批改形象工厂类,以增加对新产品的创立接口,同时还要批改具体的工厂类,否则无奈将新产品利用于零碎中。 ...

April 24, 2021 · 1 min · jiezi

关于设计模式:工厂方法模式

简略工厂模式的弊病应用简略工厂模式设计零碎,在每次减少新的对象时除了须要新增一个类型以外,还需批改工厂类的代码,违反了开闭准则,而工厂办法模式作为简略工厂模式的延申,恰好补救的简略工厂模式的缺点。 工厂办法模式对简略工厂模式加以批改,不再提供一个工厂类负责所有对象的创立,而是先定义一个形象的工厂基类,将具体对象的创立过程交给专门的工厂子类去实现,每个工厂子类都继承自形象工厂类,且实现形象工厂类中申明的办法。应用工厂办法模式能够在呈现新的产品时不批改原有的代码,通过新增一个具体的工厂类负责创立该新产品的实例即可,合乎开闭准则。 工厂办法模式的定义:定义一个创建对象的接口,然而让子类决定实例化哪一个对象。工厂办法模式让一个类的实例化提早到其子类。 工厂办法模式的构造 Product(形象产品):定义产品的接口,工厂办法模式所创立所有对象类型的基类。ProductA(具体产品类):实现了形象产品的接口,由专门的具体工厂类创立,具体产品和具体工厂关系一一对应。Factory(形象工厂):申明一个Method,返回形象产品类型。形象工厂是工厂办法模式的外围,所有具体工厂类都必须实现形象工厂类的形象办法,返回具体的产品对象。ProductAFactory(具体工厂):形象工厂的子类,实现了形象工厂的办法,返回具体的产品实例。工厂办法模式的实现//形象工厂也能够是接口abstract class FactoryMethod{ public abstract Product Build();} //具体工厂类class ProductAFactory : FactoryMethod{ public override Product Build() { return new ProductA(); }}// 形象基类abstract class Product{ public void SameMethod() { //所有产品的公共办法 Console.WriteLine("所有产品的公共办法"); } //差异化行为 public abstract void DiffMethod();}//产品Aclass ProductA : Product{ public override void DiffMethod() { //这里实现产品A的业务逻辑 Console.WriteLine("这里实现产品A的业务逻辑"); }}通过配置文件存储具体工厂类名,实现更换新的具体工厂是无需批改源代码//配置文件<?xml version="1.0" encoding="utf-8" ?><configuration> <startup> <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2" /> </startup> <appSettings> <add key="factoryName" value="Factory.ProductAFactory"/> </appSettings></configuration>static void Main(){ //读取配置文件 var factoryName = ConfigurationManager.AppSettings["factoryName"]; var factoryType = Type.GetType(factoryName); var factory = Activator.CreateInstance(factoryType) as FactoryMethod; var product = factory.Build(); product.SameMethod(); product.DiffMethod(); Console.ReadKey();}运行后果 ...

April 8, 2021 · 1 min · jiezi

关于设计模式:简单工厂模式

前言简略工厂模式属于创立型模式之一,是最常见的设计模式,利用也相当宽泛。创立型模式通过让对象的创立和应用拆散,使得开发人员在应用对象的时候无需关怀对象的创立细节,从而升高零碎的耦合度。 创立型模式该类模式在开发过程中的应用十分频繁,创立型模式关注对象的创立过程,对类的实例化进行了形象,将模块中对象的创立和应用拆散,对外暗藏了对象的创立细节。 简略工厂模式概念定义一个工厂类并提供一个办法,工厂办法能够依据参数的不同返回不同的实例,被创立的实例通常都具备雷同的父类。因为简略工厂模式中提供的办法通常是静态方法,所以该模式又能够成为动态工厂办法模式。 设计思维将须要创立的各种不同对象的相干代码封装到不同类中,这些类称为具体的产品类,再形象和提取它们公共的代码封装在一个形象产品类中,每一个具体产品类都继承自形象产品类(属于其子类)。而后提供一个工厂类用于创立各种不同的产品,在工厂类中提供一个创立产品的办法,这个办法反对传参,依据传入的参数不同创立不同的具体产品对象。使用者只须要调用工厂办法并传入对应的参数即可失去绝对的产品对象。 构造与实现结构图 由结构图可知,简略工厂模式蕴含3个角色,别离如下: Factory(工厂):工厂类,简略工厂模式的外围,负责实现创立所有产品的外部逻辑。工厂类中提供的静态方法能够间接被内部调用,并且返回形象产品类型ProductProduct(形象产品):它是工厂类所创立的所有产品的基类,封装了各种产品的公共办法ProductA&ProductB(具体产品角色):是简略工厂模式须要创立的指标,每一个具体产品都继承自Product简略工厂模式的实现将所有产品的共性移至形象产品中// 形象基类abstract class Product{ public void SameMethod() { //所有产品的公共办法 Console.WriteLine("所有产品的公共办法"); } //差异化行为 public abstract void DiffMethod();}创立具体产品类,并实现形象办法中申明的形象办法,不同的产品能够有依据需要有不同的实现//产品Aclass ProductA : Product{ public override void DiffMethod() { //这里实现产品A的业务逻辑 Console.WriteLine("这里实现产品A的业务逻辑"); }}外围工厂类//工厂类class Factory{ //静态方法 public static Product GetProduct(string type) { if (type.Equals("A")) return new ProductA(); else if (type.Equals("B")) return new ProductB(); return null; }}应用static void Main(){ var product = Factory.GetProduct("A"); product.SameMethod(); product.DiffMethod(); Console.ReadKey();}运行后果 能够发现,如果须要创立其它的具体产品对象,必须批改获取对象时传入的参数才能够实现,且代码须要从新编译,这违反了开闭准则。能够通过读取XML配置解决该问题,批改代码如下: 将须要创立的产品类型保留到配置文件App.config中<?xml version="1.0" encoding="utf-8" ?><configuration> <startup> <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2" /> </startup> <appSettings> <add key="productType" value="A"/> </appSettings></configuration>批改代码如下static void Main(){ //读取配置文件 var pType = ConfigurationManager.AppSettings["productType"]; var product = Factory.GetProduct(pType); product.SameMethod(); product.DiffMethod(); Console.ReadKey();}运行后果 ...

April 4, 2021 · 1 min · jiezi

关于oop:面向对象设计原则

面向对象设计准则的作用能够进步软件的可维护性于可复用性,其中可维护性是指代码可能被了解、改过、适应扩大的难易水平,可复用性是指代码可能被重用的难易水平,可维护性和可复用性是用于掂量软件品质的两个十分重要的属性。 7个面向对象设计准则设计准则名称定义繁多职责准则一个对象只应该蕴含繁多的职责,并且该职责被残缺地封装在一个类中开闭准则软件实体应该对扩大凋谢,对批改敞开里氏代换准则所有援用基类的中央必须都能应用基类的子类替换依赖倒转准则高层模块不应该依赖底层模块,它们都应该依赖形象。形象不应该依赖细节,细节应该依赖形象接口隔离准则接口细分,不应该依赖不须要的接口合成复用准则优先应用对象组合,而不是应用继承来达到复用的目标迪米特法令每一个对象对其它的对象都只有起码的信息,而且局限于那些与本对象密切相关的信息繁多职责准则繁多职责准则是实现高内聚、低耦合的指导方针,对一个类来说,应该仅有一个引起它变动的起因。在软件系统中,一个类承当的职责越多,它被复用的可能性就越小,并且当承当过多的职责后,就相当于将这些职责耦合在一起,当其中一个职责发生变化时,极有可能会影响到其它职责的失常工作。因而要将这些职责进行拆散,将不同的职责封装在不同的类中(将不同的变动起因封装在不同的类外面),如果多个职责总是会同时产生扭转,那么则能够将它们封装在一个类中。 开闭准则开闭准则是极其重要的设计准则,对扩大开发,对批改敞开,即尽量在不批改原有代码的状况下进行扩大。 当软件系统面临新的需要时,应该尽量保证系统的设计框架是稳固的,如果一个软件设计合乎开闭准则,那么能够十分不便的对系统进行扩大,而且在扩大时无需批改现有代码,使零碎同时具备适应性、灵活性和较好的稳定性以及延续性。 为了满足开闭准则,须要对系统进行抽象化设计,抽象化是开闭准则的要害。在C#、JAVA等编程语言中能够定义一个绝对稳固的形象层,而将不同的实现放在具体的实现层中实现。能够通过接口、抽象类等机制进行抽象化设计,定义零碎形象层,再通过具体类进行扩大,如果未来需要变动须要批改零碎的行为,毋庸对形象层进行改变,只有减少新的具体类来实现新的业务需要,在不批改原有代码的前提下扩大零碎性能,即是开闭准则。 里氏代换准则该准则示意,在软件中将一个基类对象替换成它的子类对象,程序能够失常运行,不会产生任何谬误,反之则不成立。里氏代换准则是实现开闭准则的根底,因为应用基类的中央都能够应用子类代替,因而在程序中尽量应用基类类型定义对象,在运行时再确定其子类的具体类型,用子类对象替换父类对象。 在应用里氏代换准则时应将父类设计成抽象类或者接口,让子类继承父类或实现父类接口,并实现父类中申明的办法。运行时子类实例替换父类实例,能够很不便地扩大零碎性能,无需批改原有子类代码,减少新的性能能够通过减少一个新的子类来实现。 依赖倒转准则依赖倒转准则是面向对象设计的次要实现机制之一,它是零碎抽象化的具体实现。依赖倒转准则要求:面向接口编程,不要针对实现编程。 在传递参数时或者在关联关系中尽量援用形象层,即应用接口和抽象类进行变量类型申明、参数类型申明、办法返回类型申明和数据类型转换等,而不要用具体类来做这些事件。为了确保该准则的利用,一个具体类该当只实现接口或抽象类中申明过的办法,而不要给出多余的其它办法,否则基类将无奈调用到子类中减少的新办法。 在引入形象层后,零碎将会具备很好的灵活性,在程序中应该尽量应用形象层进行编程,而将具体类写在配置文件中,这样做的益处在于,如果未来需要发生变化,只须要对形象层进行扩大,并批改配置文件,而无须批改原有的代码,满足开闭准则。 实现依赖倒转准则,须要针对形象层进行编程,将具体类的对象通过依赖注入的形式注入其它对象中。依赖注入指的是:当一个对象须要与其它对象产生依赖关系时,通过办法参数来注入所依赖的对象。罕用的注入形式:结构注入(通过构造函数传入具体类的对象)、设置注入(set办法传入具体类的对象)和接口注入(在接口中申明的业务办法传入具体类的对象)。 接口隔离准则当一个接口太大时,须要将它宰割成更细小的接口,每一个接口应该承当一种绝对独立的角色,不做与之无关的事件。 接口细分是有益处的,如果提供的接口太大,蕴含的办法太多,应用起来反而不不便,因为实现接口就须要实现这个接口的所有办法,而并不是每一个办法都是所须要的。这时候为了使职责繁多,须要将接口中的办法依据其职责别离放在不同的小接口中,确保接口应用起来不便,并且都承当繁多的角色。接口应该尽量细化,每个接口中蕴含的办法应尽量的少,最好只蕴含一个模块所有要的办法即可。 合成复用准则合成复用准则就是在一个新的对象里通过关联关系来应用一些已有的对象,使之成为新对象的一部分,复用时尽量应用组合,少用继承。 在面向对象中,能够应用组合或者继承的形式复用已有的设计和实现,但首先应该思考组合,因为这种形式能够使零碎更加灵便,升高类与类之间的耦合度,一个类的变动对其它类的影响较小。其次才思考继承,应用继承时应该恪守里氏代换准则。 继承的局限性:如果基类产生扭转,那么子类也不得不发生变化。同时继承的实现是动态的,不可能在运行时产生扭转,短少灵活性。 为什么应用组合:因为组合是将已有对象引入到新对象中,让它成为新对象的一部分,因而新对象能够调用已有对象的性能,而对象外部的实现对新对象来说并不可见。比照继承,组合的耦合度更低,成员对象的变动对新对象的影响不大,并且合成复用能够在运行时动静进行,新对象能够动静援用成员对象类型雷同的其它对象。 什么时候用继承什么时候用组合?如果两个类之间的关系是a-Has-b(a对象领有b职责),那么应该应用组合;如果关系是a-Is-b(a对象属于b类型的某一品种),这是应该应用继承。 迪米特法令每个模块应该尽量少地与其它模块互相援用产生关联,这样在模块变动时,对其它模块产生的影响也会最小,利用这个法令可升高类之间的耦合度。 对象只应该与本人所关怀的成员通信,如: 对象自身,即本人以参数模式传入到以后对象中的对象以后对象的成员对象以后对象所创立的对象只有数据上述的某一个类型,便可与之间接交互。在程序设计的时候,如果两个对象没有必要彼此间接通信,那么这两个对象就不应该产生任何间接关联,如果一个对象须要调用另一个对象的办法,能够通过引入一个两头者的形式来实现。 利用迪米特法令时,应尽量创立松耦合的类,耦合度越低,越利于复用;在类的结构设计上,该当升高每一个类中成员变量和成员函数的拜访权限;类型该当尽量设计成不变类;一个对象对其它对象的援用该当降到最低。

March 15, 2021 · 1 min · jiezi

关于oop:Go-去找个对象吧

前言我的读者中应该大部分都是 Java 从业者,不晓得写 Java 这些年是否真的有找到对象? 没找到也没关系,总不能在一棵树上吊死,咱们也能够来 Go 这边看看,说不定会有新发现。 开个玩笑,本文会以一个 Javaer 的角度来聊聊 Go 语言中的面向对象。 <!--more--> OOP面向对象这一词来源于Object Oriented Programming,也就是大家常说的 OOP。 对于 Go 是否为面向对象的编程语言,这点也是探讨已久;不过咱们能够先看看官网的说法: 其余的咱们暂且不看,Yes and No. 这个答复就比拟奥妙了,为了这篇文章还能写下去咱们先认为 Go 是面向对象的。 面向对象有着三个重要特色: 封装继承多态封装Go 并没有 Class 的概念,却能够应用 struct 来达到相似的成果,比方咱们能够对汽车申明如下: type Car struct { Name string Price float32}与 Java 不同的是,struct 中只存储数据,不能定义行为,也就是办法。 当然也能为 Car 定义方法,只是写法略有不同: func (car *Car) Info() { fmt.Printf("%v price: [%v]", car.Name, car.Price)}func main() { car := Car{ Name: "BMW", Price: 100.0, } car.Info()}在办法名称前加上 (car *Car) 便能将该办法指定给 Car ,其中的 car 参数能够了解为 Java 中的 this 以及 Python 中的 self,就语义来说我感觉 go 更加简略一些。 ...

February 23, 2021 · 2 min · jiezi

关于oop:面向对象类和对象封装继承多态抽象接口枚举反射String

类和对象类文件后缀:.java, 包(namespace):我的项目上右键创立 package _17bang.CD.Yz; 引入(using),留神import的是一个“类”,不是名称空间 import _17bang.YZ.Student; 但能够应用通配符 *,比方: import _17bang.YZ.*; 没有partial类 拜访修饰符:没有关键字internal(不能显式申明,默认即可,package中可见) 一个类文件只能有一个public类 static:能够由对象调用(只warning不报错) 援用类型和值类型其余类成员快捷键生成:alt+shift+s, 构造函数间调用: public Student(int age) { this(); } 没有属性,只有getter和setter。所以也没有主动属性等一系列相干的语法。 private int age; public int getAge() { return age; } public void setAge(int age) { this.age = age; } 没有readonly关键字 final: 润饰变量/字段时,等同于 const 没有索引器 析构函数 protected void finalize() { } 动态和实例没有static构造函数 顶级类不能static,外部类能够 继承应用关键字extends示意继承,super示意基类 public class OnlineStudent extends Student { public OnlineStudent(int age) { super(age); } } ...

December 26, 2020 · 1 min · jiezi

关于oop:函数式编程如何高效简洁地对数据查询与变换

摘要:一提到编程范式,很容易联想到宗教的虔诚,每种宗教所表白信条都有肯定合理性,但如果始终只遵循一种教条,可能也被让本人痛苦不堪,编程范式也是如此。案例1案例一,代码摘抄来自一企业培训资料,次要代码逻辑是打印每课问题,并找出学生非F级别课程统计均匀分数: class CourseGrade { public String title; public char grade;}public class ReportCard { public String studentName; public ArrayList<CourseGrade> cliens; public void printReport() { System.out.println("Report card for " + studentName); System.out.println("------------------------"); System.out.println("Course Title Grade"); Iterator<CourseGrade> grades = cliens.iterator(); CourseGrade grade; double avg = 0.0d; while (grades.hasNext()) { grade = grades.next(); System.out.println(grade.title + " " + grade.grade); if (!(grade.grade == 'F')) { avg = avg + grade.grade - 64; } } avg = avg / cliens.size(); System.out.println("------------------------"); System.out.println("Grade Point Average = " + avg); }}下面的代码有哪些问题呢: ...

November 16, 2020 · 2 min · jiezi

关于oop:Java基础总结2面向对象

1.面向对象(1)概念:所谓的面向对象是一种编程思维,通过这种思维能够把生存中的简单事件变得简单化,从原来的执行者变成了指挥者,面向对象是基于面向过程而言的。咱们常常说的面向对象的编程实现(OOP,Object Oriented Programming) 面向对象强调后果,我的了解就是面向对象是将咱们生存中的各种各样的货色封装成为一个对象,其中包含了他的各种属性和各种办法,对外提供这些办法和属性,使得咱们能够通过创立(new)这个对象去调用这些办法和属性,这样即便咱们不晓得各种属性和各种办法是如何封装的咱们也能够应用这些办法和属性。(2)三大特色: 1、封装:封装就是将一个对象的属性和操作联合成一个独立的整体,并尽量暗藏其外部的操作细节。 2、继承:继承就是,子类继承父类的特色和行为,使子类具备父类的一些办法。然而父类中private润饰的办法,不能被子类具备,而且父类中static润饰的办法,不能被子类重写,即使子类中有与父类中同名的静态方法,二者也没有任何关联。 3、多态:多态是同一个行为具备多个不同表现形式或状态的能力。多态就是同一个接口,应用不同的实例而执行不同操作,多态性是对象多种表现形式的体现。当应用多态形式调用办法时,首先查看父类中是否有该办法,如果没有,则编译谬误;如果有,再去调用子类的同名办法。多态的益处:能够使程序有良好的扩大,并能够对所有类的对象进行通用解决。 2.构造方法(1)概念:构造方法是一种非凡的办法,它是一个与类同名且返回值类型为同名类类型的办法。对象的创立就是通过构造方法来实现,其性能次要是实现对象的创立或者对象的初始化。当类实例化一个对象时会主动调用构造方法。构造方法和其余办法一样也能够重载。(2)模式:能够有参也能够无参 ``修饰符 类名([参数]){ 代码……}`` 3.面向对象中的关键字(1)private:是一个权限修饰符,用于润饰成员变量和成员函数,被私有化的成员只能在本类中拜访,想要批改或获取该成员,只能对外提供公共的get和set办法。(2)this和super:this代表本类对象的一个援用对象。构造函数中,this()必须放在第一行。(3)extends:是继承的关键字,示意子类继承父类时应用,子类继承父类的特色和行为,使子类具备父类的一些办法。然而父类中private润饰的办法,不能被子类具备,而且父类中static润饰的办法,不能被子类重写,即使子类中有与父类中同名的静态方法,二者也没有任何关联。这也是面向对象的三大特色之一。(4)static:动态关键字,能够润饰成员变量、成员办法以及动态代码块(随着类的加载而加载,并且只被加载一次,个别用于我的项目的初始化),特点是被润饰的变量及办法会随着类的加载而加载,优于对象加载,只加载一次就会始终存在,不会再开拓新的空间,全局惟一全局共享,能够间接被类名调用,另外动态只能调用动态,非动态能够随便调用,static不能喝this或者super独特应用,因为有static时可能还没有对象。(5)final:final能够润饰类、办法和成员变量,特定是被final润饰的类,不能被继承,被final润饰的办法不能被重写,被final润饰的变量是个常量,值不能被更改。(6)abstract:形象的关键字,用来润饰类或办法,Java中能够定义没有办法体的办法,该办法由其子类来具体的实现。该没有办法体的办法咱们称之为形象办法,含有形象办法的类咱们称之为抽象类。抽象类能够了解为是一个只有办法申明没有办法体的非凡类。特点是抽象类中能够没有形象办法(有子类去实现),如果类中有形象办法,那该类必须定义为一个抽象类,子类继承了抽象类当前,要么还是一个抽象类,要么就把所有形象办法都重写,抽象类不能够被实例化。 4.接口(interface)(1)概念:Java外面因为不容许多重继承,所以如果要实现多个类的性能,则能够通过实现多个接口来实现。 Java接口和Java抽象类代表的就是形象类型,就是咱们须要提出的形象层的具体表现。OOP面向对象的编程,如果要进步程序的复用率,减少程序的可维护性,可扩展性,就必须是面向接口的编程,面向形象的编程,正确地应用接口、抽象类这些太有用的形象类型做为java构造档次上的顶层。(2)特点:1、接口中都是形象办法2、通过interface关键字创立接口3、通过implements让子类来实现4、能够了解成,接口是一个非凡的抽象类5、接口冲破了java的单继承的局限性6、接口和类之间能够多实现,接口和接口之间能够多继承7、接口是对外裸露的规定,是一套开发标准8、接口进步了程序的性能扩大,升高了耦合性9、接口里是没有构造方法的。在创立实现类的对象时默认的super(),是调用的默认Object的无参结构。10、接口里没有成员变量,都是常量。所以,你定义一个变量没有写修饰符时,默认会加上:public static final11、接口里的办法,默认就都是形象的,如果你不写明是abstract的,那会主动补齐12、Java中单继承的局限性通过接口能够解决。接口能够多继承也能够多实现,甚至能够继承的同时多实现。

November 10, 2020 · 1 min · jiezi

关于oop:面向对象

Js是一门弱类型语言 Java、c、c++、c#、php都是强类型语言 在强类型语言中有一个‘类’的概念,是用来实现继承的。 强类型语言:三种个性 1. 多态性:在强类型语言中,一个变量存在多种状态。在不同的上下文代码中,变量的模式能够不同(理解) 2. 继承性:在强类型语言中,会存在类的概念,类有父级和子级关系(相似于js中的函数),子级类能够齐全继承父级类中的所有属性 (1) 继承就是完完全全领有 3. 封装:封装函数 Js在诞生初期没有继承 因为js中没有类 起初人们就创立出了面向对象编程 1. js中的继承 (1) 原型 ① 原型是基于对象呈现的 ② 定义:原型就是某一个对象中__proto__非标准属性指向的一个对象 ③ __proto__非标准属性 是每一个对象中的默认属性,不须要手动编写,主动就会有这个属性,这个属性的属性值就是这个对象的原型 ④ 原型:其实也是一个对象,也会有各种属性,这些属性,会齐全继承给原型的对象 ⑤ 对象.__proto__就是以后对象的原型 (2) 对象的申明形式 ① new关键字申明 ② 字面量申明 ③ 由以上两种办法可知 1) Js中申明对象能够间接申明,也能够通过构造函数来申明 2) 这里,咱们次要探讨一下通过构造函数申明对象 (3) 构造函数 ① 构造函数能够实例化对象,这个实例化的对象有原型 ② 实例化的对象原型也是 对象名.__proto__ ③ 对象名.__proto__(原型)和构造函数之间的关系: 1) 原型中会有一个属性constructor,这个属性的属性值就是对象的构造函数 a. 原型.constructor===构造函数 b. 构造函数.prototype===原型 构造函数和工厂函数构造函数中最初会隐式返回绑定好属性的空对象,如果用户没有写返回值或者返回值是值类型数据,那么,构造函数就会隐式返回。如果用户写了返回值为援用类型,构造函数就不会隐式返回,而是返回用户写入的返回值 对象的创立办法1. 间接创建对象 (1) 长处:简略,明了 (2) 毛病:当须要创立大量对象的时候,会造成大量的工作量=-= 2. 构造函数创建对象 ...

September 9, 2020 · 1 min · jiezi

关于oop:对面向对象的进一步认识

前言这几天通过实现issue对面向对象有了新的意识。 对象援用在接触一个子组件父组件的时候,自组件扭转了对象的值,然而并没有传给父组件,最初在父组件保留失效了,那么,他是如何将值传给父组件的呢。通过潘老师的领导,我意识到对象与变量的不一样。首先,咱们这里父组件通过@input形式传给子组件了part对象。 @Input() part: Part;而自组件扭转part对象的某个属性,父组件里的part对象是跟着变得,换句话说,子组件与父组件为援用的为同一个对象。咱们看一下《Head First Java》对此内容的介绍,这几张图形象的阐明了对象援用的规定。通过这张图,咱们能够假如@input作用相当于 part(子组件part对象) = _part(父组件part对象)这时候咱们就不难理解为什么子组件对象的某一属性扭转而父组件对象属性为什么跟着扭转了。正如第二个图示的那样,咱们子组件与父组件的变量都指向了同一对象。当一者将对象进行扭转时,另一个人再去读取对象,对象曾经扭转了。让咱们用一个demo来验证 public class Dog { public int weight;}class dogTest { @Test void test() { Dog b = new Dog(); Dog c = new Dog(); c = b; System.out.println(c.weight); b.weight = 10; System.out.println(c.weight); }}正如后面所说,如果两个变量指向同一个对象,那么通过其中一个扭转对象的属性,另一个变量也会扭转。 面向对象三大个性之一——封装咱们大一学面向对象编程思维的时候常背的就是面向对象三大个性,然而什么是封装没学明确。其实咱们的我的项目始终在应用封装,比方咱们在后盾加一个字段,通常状况下是在实体类新建一个属性,而后再给他get办法和set办法,这种做法便是封装。 public class Dog { private int weight; public int weight() { return weight; } public void setWeight(int weight) { this.weight = weight; }}咱们留神到,咱们将属性设为公有属性,而通过私有的weight与setWeight办法进行读取与设置。这是为什么呢其实咱们当初还没有体现封装的长处。咱们察看这个类,weight代表狗的分量,分量就应该是一个大于0的值。如果咱们设置weight为私有属性并通过dog.weight传值,咱们就有可能传入一个谬误的值 ...

August 1, 2020 · 1 min · jiezi

关于面向对象的深入理解

关于面向对象的深入理解引言第一次接触面向对象是在大一下学期的Java程序语言设计这门课上,听了老师讲了各种名词:类、对象、接口、继承、多态、封装……。当时也是一知半解,只是知道有这些东西,用的时候也是依葫芦画瓢,并不能理解其真正的设计内涵。随着项目经验的增加,也逐步体会出些许程序设计的精妙所在,再加之读各类书籍有所心得,故准备写这一些列文章,分享交流。 如果把程序员比作剑客的话,那么有两件东西最为重要:宝剑与剑术。宝剑可以理解为各种实打实的硬技能,比如:Java、MySQL、SpringBoot……,这些东西是死的,知识点很明确,及时与别人有所差距,下点功夫总能赶得上;而剑术则是程序设计之道,比如:需求分析、架构设计,这些东西相对来回活泛了许多,因为在这个过程中你所面对的不再是热腾腾的机器,而是各种各样的客户、复杂的业务逻辑和想法各异的同事们。所以,码农与程序员的差别就在于程序设计方面。设计本身就是一门艺术,那些统领千万代码大军的CTO们,是运筹帷幄的将军也是才华横溢的艺术家啊! 从面向机器到面向对象最开始的计算机程序编码就是0101,完全要以机器的二进制思维去思考问题;后来有了汇编只是编码形式上有了变化,但是本质并没有改变,程序员们的关注点还是在于机器本身,程序员要对CPU、寄存器等硬件了如指掌;当C语言、COBOL等面向过程的语言横空出世后,这是一次本质上的革命,把程序员从二进制的世界中解救了出来,终于可以让他们“像人一样”去思考问题啦。 此时程序员可以专注于问题本身,理清解决问题的具体步骤,然后将之用程序语言讲给计算机就可以啦。对于单一、具体的问题无论其复杂与否,采用面向过程的语言都可以完全hold住。这就如同流水线作业一般,环环相扣,问题复杂了无非就是工序多一些而已,整体来看难度不大。但是随着计算机硬件的发展,人们希望用计算机处理的事务也越来越多,业务逻辑也越来越复杂,此时再用面向过程的语言去实现如此纷乱的业务逻辑就有些难以招架了。就如同好多条生产流水线掺杂在了一起,A线依赖B线上的一道工序,B线又依赖于C线的一道工序,而C线还和A线有着关联;这样杂乱的流水线普通主管看了肯定会头大。此时就需要面向对象的程序设计语言来大杀四方了。 面向对象这种设计思想更加适合对于现实世界的模拟,更加适合处理一个系统性问题。它不再是以具体的点为中心,而是着眼于整个面。用类抽象出一群事物的共性,用接口定义好不同事物之间的交流规则,用继承的方式让同一类的事物也可以千姿百态,用多态的方式让千姿百态的事物也可以整齐划一。每个程序员都是代码世界的上帝,以面向对象的思想去设计整个世界的架构,以面向过程的思想去研究世界的每一株土木,方可达到一花一世界,一叶一菩提的境界。 面向对象三剑客类物以类聚,人以群分。——《战国策·齐策三》类是整个面向对象设计理论的基石,只有把类搞清楚搞明白了才能让整个理论大厦有坚实的地基。 首先抛开程序设计,单从现实世界的角度来看:枪保卫祖国的人叫军人,在工地上干活的人叫工人,在高级写字楼里面办公的人叫白领。对于每一类人,他们每个个体都不相同,但是它们都有相似的特征,所以我们把他们归为的一类人。面向对象最根本的思想就是对于现实世界的模拟,用程序语言来表述业务逻辑。所以程序中的类可以这样理解:类是一组相似事物的统称,是对他们共性的一种归纳。 类具有属性和方法,从军人的角度来看他们所属于的部队、所驻守的地区……,这是军人的属性;他们可射击、可擒拿……这是他们的方法。 实际上世间万物皆可归于一类,因为只要有相似点,就有依据把他们归于一类。那应该如何来设计类呢?请记住这样一句话:横看成岭侧成峰,远近高低各不同。角度决定看法,一定要结合具体需求,来考虑类的设计。 类有属性和方法,那么如何划分属性和方法呢?这里应该遵循极简原则。即属性原子化、方法单一化。属性细化到不能再分割,方法只能做一件事。这样能够最大化地解耦,满足系统的可拓展性与易修改性。 对象对于类的认知清晰以后,对象就比较容易理解了。对象就是类的一个具体实例。以军人为例,黄继光,邱少云就是军人这一类人当中一个具体的实例。 其实关于类和对象还可以深入探究一下,为什么有了类还会有对象?类和对象各自的作用是什么?这个时候我们还是要回归于现实世界。中国的人很多,那么党和政府如何来组织管理我们呢?首先不同的人有不同的职业,比如医生、教师、军人、工人等等。政府依据不同类别的职业制定出了不同的政策,比如军人买票优先、教师游览一些景点免费。其实依据不同职业有不同治理策略是一种归类更是一种抽象。 抽象的本质就是隔离干扰、总结共性、降低事物的复杂度,站在更高的角度去看待问题,从而更好地把控全局。所以类对于程序员来说是一种抽象的工具,让我们能够更好地全局把控整个项目,组织管理整个项目。党和国家领导人提出了全面建设小康社会,提出社会主义核心价值观等理论,有些普通民众看起来是空话套话,原因他们的视野只局限于自己的一亩三分地,看到的是具体的事务,而习大大则是心系整个国家,他说的话是立足于整个中国,对于亿万华夏儿女都有用的话。所以习大大是对中国国情高度总结归纳抽象后才能提出这样的理论! 对于程序员来说,类是抽象的,对象是具体的,拥有了类就拥有了上帝视角,来俯瞰整个系统;拥有了对象就拥有的亿万子民,来进行劳动,创造价值。 接口“接口”这个词是被滥用、乱用最多的术语了。抛开其他的接口不谈,这里主要是说一下面向对象中的接口。如果只看教科书上的定义,你很容易明白接口具体是什么: Java接口是一系列方法的声明,是一些方法特征的集合,一个接口只有方法的特征没有方法的实现,因此这些方法可以在不同的地方被不同的类实现,而这些实现可以具有不同的行为(功能)。—— 百度百科但是光看这些冷冰冰的定义,却很难让人明白,尤其是对新手来说,为什么要用到接口?接口本质作用是什么?为了了解这个,还是得再次回归到现实世界中。想想有身边哪些接口:Micro usb、Type-C、HDMI……,为什么要用到这些接口?因为手机要连接电源,显示器要连接电脑……,从中我们进行一次抽象总结:接口是两种不同事物交互时的一个关卡,更是一组标准,定义了交互双方所应该遵循的规则。从程序的角度来看:这种规则就是一组相关的功能点的集合。举个栗子:目前有一个鼠标类,当某天我们这个鼠标类的对象需要与主机类的对象进行交互时,此时就应该去实现一个叫USB的接口,方可进行交互。 从设计的角度来看:接口着眼于某种具体的功能,是一种规范与约束;类着眼于某一类事物的共性,是一种总结与抽象。 三大核心特征封装这个概念从字面上很容易理解,就是把一堆东西封闭包装起来。现在我们来深入探究一下其设计内涵。在我看来封装的好处主要体现在两点:对外降低复杂度,对内保护数据。其实对于个人开发者来说,一个人写整个项目,第二点好处并不一定能体会的到。尤其是写属性的get()与set()方法之时,总是感觉多此一举。但是当面对系统性项目需要多人协同工作时,封装的意义就体现出来了。 第一:可以专人干专事,每个人管好自己的一亩三分地就OK了,对外暴露自己的实现的功能就行了,而用别人的模块时也只需要调用相应方法就行了,不用操心其具体实现。 第二:可以防止坏人做坏事,保护核心业务。比如小黑在一个银行系统项目组中做取款流程开发,当客户取过钱后小黑需要调用项目主管所提供的 public Boolean updateBalance(Float money,Int operation)这一方法去修改余额,而不是能够直接访问用户余额这一关键属性。如果不进行封装,当某天主管怒斥了小黑一顿,小黑想搞点破坏,是不是可以把代码修改成用户取钱后,不进行减操作,而是直接访问用户余额这一属性给他加上一些钱哪。 继承继承这一机制估计是大家用得最多的了,通过继承我们可以很方便地对父类进行修改与拓展。从设计者的角度来看继承是从抽象到具体的一个过程,架构阶段我们基于主要业务逻辑构建出代码的主框架,建好各种基类,这是一次归纳抽象的过程,细节设计阶段我们根据具体业务逻辑与基类,来构造出各种子类,这是一次展开具体化的过程。 多态多态从字面上很容易理解,就是多种形态嘛。孙大圣七十二变,无论是变成石头、老头、大树其本质上都还是个孙悟空,紧箍咒都还对他管用。这样的好处就是可以屏蔽掉子类对象的差异,使得程序员可以写出通用性的代码,而无需针对不同的子类写不同的代码,让代码更易于拓展。 唐僧的紧箍咒对于孙悟空本身是一个方法,只要一念咒语猴子就头痛难忍,后来孙悟空变成了一个老头,唐僧不用专门去针对老头形态的孙悟空去研究一套紧箍咒,只用念原来的依然奏效。这样对于唐僧来说紧箍咒就具备的通用性与可拓展性,无论你猴子怎么变,唐僧都能以不变应万变! 总结宝剑都是定型的,而剑术确是灵活的,虽然剑术也有一套规则,但在实战中是可以千变万化,形态万千。所谓好马配好鞍,互联网技术日臻成熟,作为开发者的我们有着数不清的宝剑,但拥有再厉害的宝剑也得配合一套自己了熟于心的剑术才能发挥其威力。程序员也如同剑客一般,剑客之道有三重: 手中有剑,心中有剑,人剑合一,第一境界; 手中无剑,心中有剑,杀人于无形,第二境界; 手中无剑,心中无剑,不武而屈人之兵,此乃第三境界! 不知道作为程序员的我们现在到哪中境界了哪?

October 17, 2019 · 1 min · jiezi

前端理解依赖注入控制反转

前端的技术的极速发展,对前端同学来说也是一个不小的挑战,有各种各样的东西需要学,在开发过程中经常会被后端同学嘲讽,对于前端来讲根本就不存在类的概念,很多时候需要把大量的业务代码堆积在页面或者组件中,使组件和页面变得特别的臃肿,一旦业务逻辑复杂的情况下,及时组件化做的很好,仍然避免不了难以维护。 之所以会被后端同学嘲讽,一基础掌握不扎实,对前端理解不到位,二缺乏面向对象思想,三对业务与基础傻傻分不清楚。ECMAScript 2015与Type Script的推出,提出了一个很重要的概念就是class(类)的概念。在没有class之前为了前端能够有类的概念,一直都是使用构造函数模拟类的概念,通过原型完成继承。 虽然前端提出了很多概念(模块化,组件化...),个人觉得面向对象的应用是前端对于项目以及整体架构来讲是一件利器,代码结构好与坏与面向对象有一定的关系,但不是全部。不过我们可以借助计算机领域的一些优秀的编程理念来一定程度上解决这些问题,接下来简单的说下依赖注入(控制反转)。 什么是依赖注入依赖注入一般指控制反转,是面向对象编程中的一种设计原则,可以用来减低计算机代码之间的耦合度。其中最常见的方式叫做依赖注入,还有一种方式叫依赖查找。通过控制反转,对象在被创建的时候,由一个调控系统内所有对象的外界实体将其所依赖的对象的引用传递给它。也可以说,依赖被注入到对象中。 从上面的描述中可以发现,依赖注入发生在2个或两个以上类,比如现在有两个类A与B类,如果A是基础类存在的话,B做为业务类存在,B则会依赖于A,上面有一句话很重要由一个调控系统内所有对象的外界实体将其所依赖的对象的引用传递给它,个人理解,在B类中使用A类的实例,而是继承A类。 对面向对象了解的同学应该了解,面向对象7大原则: 单一职责开闭原则里氏替换依赖倒置接口隔离迪米特法则组合聚合复用原则详细解释参照:面向对象之七大基本原则(javaScript) 然而使用依赖注入的事为了降低代码的耦合程度,提高代码的可拓展性。以上都是一些面向对象的思想,我们参考一下以上最重要的几个原则,层模块不应该依赖低层模块。两个都应该依赖抽象,抽象不应该依赖具体实现,具体实现应该依赖抽象。 // 球队信息不依赖具体实现// 面向接口即面向抽象编程class Fruit { constructor(name) { this.name = name }}class Tropical { // 此处的参数,是teamInfo的一个实例,不直接依赖具体的实例 // 面向抽象 constructor(fruit) { this.fruit = fruit; } info() { console.log(this.fruit.name) }}// 将依赖关系放到此处来管理,控制权也放到此处// Tropical和Fruit之间不再有直接依赖// 原本直接掌握Fruit控制权的Tropical不再直接依赖// 将依赖控制,落在此处(第三方模块专门管理)即为控制反转var ym = new Tropical(new Fruit('香蕉'))ym.info()var kobe = new Tropical(new Fruit('菠萝'))kobe.info()依赖注入的作用初始化被依赖的模块 如果不通过依赖注入模式来初始化被依赖的模块,那么就要依赖模块自己去初始化了那么问题来了:依赖模块就耦合了被依赖模块的初始化信息了 注入到依赖模块中 被依赖模块已经被其他管理器初始化了,那么依赖模块要怎么获取这个模块呢? 有两种方式: 自己去问别人主动给你没用依赖注入模式的话是1,用了之后就是2 想想,你需要某个东西的时候,你去找别人要,你需要提供别人什么信息?最简单的就是那个东西叫什么,即你需要提供一个名称。所以,方式1的问题是:依赖模块耦合了被依赖模块的名称还有那个别人而方式2解决了这个问题,让依赖模块只依赖需要的模块的接口。 依赖注入的优点依赖注入降低了依赖和被依赖类型间的耦合,在修改被依赖的类型实现时,不需要修改依赖类型的实现,同时,对于依赖类型的测试。依赖注入方式,可以将代码耦合性降到最低,而且各个模块拓展不会互相影响, 实现数据访问层,也就是前端你的数据请求层模块与接口重构,依赖注入背后的一个核心思想是单一功能原则,这意味着这些对象在系统的任何地方都可以重用。随时增加单元测试,把功能封装到整个对象里面会导致自动测试困难或者不可能。将模块和接口与特定对象隔离,以这种方式重构可以执行更先进的单元测试。Vue中使用上面写的例子也只是对依赖注入见单的使用,在项目过程中往往就不是这么简单了,肯定不会向例子这么简单,而是很复杂很庞大的一个项目。项目中分为各种各样的模块,这种情况又改如何处理?在JavaScript中常见的就是依赖注入。从名字上理解,所谓依赖注入,即组件之间的依赖关系由容器在运行期决定,形象的来说,即由容器动态的将某种依赖关系注入到组件之中。 前端项目中并不像后端一样,各种各样的类,虽然前端可以写class,若是React项目的话,还会好很多,由于其框架使用,全部是基于class但是在vue项目中,又应该怎么具体体现呢?页面中的业务也是可以作为类存在最终注入到Vue页面中,最终完成业务逻辑。 通过依赖注入到底想要达到到什么效果呢,依赖注入最终想要达成的效果则是,页面的表现与业务相脱离,从本质上来说,页面的展现形式与业务逻辑其实没有根本联系的。若使用这种依赖注入的形式,则可以轻松的把业务和页面表现分离开,页面更加的专注于展示,而所注入的东西则更加的专注于业务的处理。项目也则会变得容易维护。 index.vue <template> <div> <el-button @click="getList" :loadding="loadding">获取表格数据</el-button> <ul> <li v-for="(item,index) of list" :key="index">{{item}}</li> </ul> </div></template><script>import operation from "@/business/index/Operation.js";export default { data() { return { list: [], query:{}, loadding:false } }, methods:{ async getList(){ let {query} = this; this.loadding = true; try{ this.list = await operation.getData.call(this,query); }catch(error){ console.log(error) } this.loadding =false; } }}</script><style>@import "@/style/index.vue";</style>operations.js ...

September 7, 2019 · 1 min · jiezi

面向对象之七大基本原则javaScript

面向对象编程有自己的特性与原则,如果对于面向对象有一些了解的话,面向对象三大特征,封装、继承、多态,如果对面向对这三个概念不太了解,请参考面向对象之三个基本特征(javaScript) 单一职责如果我们在编写程序的时候,一类或者一个方法里面包含了太多方法,对于代码的可读性来说,无非是一场灾难,对于我们来说。所以为了解决这个问题,出现了单一职责。 什么是单一职责单一职责:又称单一功能原则,面向对象五个基本原则(SOLID)之一。它规定一个类应该只有一个发生变化的原因。(节选自百度百科) 按照上面说的,就是对一个类而言,应该仅有一个引起它变化的原因。换句话说,一个类的功能要单一,只做与它相关的事情。在类的设计过程中要按职责进行设计,彼此保持正交,互不干涉。 单一职责的好处类的复杂性降低,实现什么职责都有清晰明确的定义可读性提高,复杂性降低,那当然可读性提高了可维护性提高,可读性提高,那当然更容易维护了变更引起的风险降低,变更是必不可少的,如果接口的单一职责做得好,一个接口修改只对相应的实现类有影响,对其他的接口无影响,这对系统的扩展性、维护性都有非常大的帮助。实例class ShoppinCar { constructor(){ this.goods = []; } addGoods(good){ this.goods = [good]; } getGoodsList(){ return this.goods; }}class Settlement { constructor(){ this.result = 0; } calculatePrice(list,key){ let allPrice = 0; list.forEach((el) => { allPrice += el[key]; }) this.result = allPrice; } getAllPrice(){ return this.result; }}用上面的代码来说ShoppinCar类存在两个方法addGoods和getGoodsList,分别是添加商品和获取商品列表。Settlement类中存在两个方法calculatePrice和getAllPrice分别做的事情是计算价钱与获取总价钱。ShoppinCar与Settlement都是在做自己的事情。添加商品与计算价格,虽然在业务上是相互依赖的,但是在代码中分别用两个类,然他们自己做自己的事情。其中任何一个类更改不会对另一个类进行更改。 开闭原则在一个类中暴露出去的方法,若这个方法变更了,则会产生很大的后果,可能导致其他依赖于这个方法且有不需要变更的业务造成大面积瘫痪。为了解决这个问题,可以单独再写一个方法,若这个方法与这个类中的其他方法相互依赖。 解决办法: 把其中依赖的代码copy一份到新的类中。在新类中引用旧类中的方法。两种方法都不是最好的解决方案。 第一种方法会导致代码大量的重复,第二种方法会导致类与类之间互相依赖。 什么是开闭原则开闭原则:“软件中的对象(类,模块,函数等等)应该对于扩展是开放的,但是对于修改是封闭的”,这意味着一个实体是允许在不改变它的源代码的前提下变更它的行为。(节选自百度百科) 开闭原则对扩展开放,对修改关闭,并不意味着不做任何修改,底层模块的变更,必然要有高层模块进行耦合,否则就是一个孤立无意义的代码片段。开闭原则是一个最基本的原则,另外六个原则都是开闭原则的具体形态,是指导设计的工具和方法,而开闭原则才是精神领袖. 开闭原则好处开闭原则有利于进行单元测试开闭原则可以提高复用性开闭原则可以提高可维护性面向对象开发的要求实例class Drag { down(){ // ... } move(){ // ... // 对拖拽没有做任何限制可以随意拖拽 } up(){ // ... } }class LimitDrag extends Drag { move(){ // ... // 重写该方法对拖拽进行限制处理 }}在LimitDrag中重写了move方法,若修改了可以满足两种需求,一种是限制型拖拽,一种是不限制型拖拽,任何一个更改了另外一个还是可以正常运行。 ...

September 7, 2019 · 3 min · jiezi

面向对象基本原则3-最少知道原则与开闭原则

面向对象基本原则(3)- 最少知道原则与开闭原则五、最少知道原则【迪米特法则】1. 最少知道原则简介最少知识原则(Least KnowledgePrinciple,LKP)也称为迪米特法则(Law of Demeter,LoD)。虽然名字不同,但描述的是同一个规则:一个对象应该对其他对象有最少的了解。 通俗地讲,一个类应该对自己需要耦合或调用的类知道得最少,你(被耦合或调用的类)的内部是如何复杂都和我没关系,那是你的事情,我就知道你提供的这么多public方法,我就调用这么多,其他的我一概不关心。 2. 最少知道原则实现只与直接关联的类交流每个对象都必然会与其他对象有耦合关系,耦合关系的类型有很多,例如组合、聚合、依赖等。 出现在成员变量、方法的输入输出参数中的类称为直接关联的类,而出现在方法体内部的类不属于直接关联的类。 下面举例说明如何才能做到只与直接关联的类交流。 场景:老师想让班长清点女生的数量 Bad/** * 老师类 * Class Teacher */class Teacher { /** * 老师对班长发布命令,清点女生数量 * @param GroupLeader $groupLeader */ public function command(GroupLeader $groupLeader) { // 产生一个女生群体 $girlList = new \ArrayIterator(); // 初始化女生 for($i = 0; $i < 20; $i++){ $girlList->append(new Girl()); } // 告诉班长开始执行清点任务 $groupLeader->countGirls($girlList); }}/** * 班长类 * Class GroupLeader */class GroupLeader { /** * 清点女生数量 * @param \ArrayIterator $girlList */ public function countGirls($girlList) { echo "女生数量是:", $girlList->count(), "\n"; }}/** * 女生类 * Class Girl */class Girl {}$teacher= new Teacher();//老师发布命令$teacher->command(new GroupLeader()); // 女生数量是:20上面实例中,Teacher类仅有一个直接关联的类 -- GroupLeader。而Girl这个类就是出现在commond方法体内,因此不属于与Teacher类直接关联的类。方法是类的一个行为,类竟然不知道自己的行为与其他类产生依赖关系,这是不允许的,违反了迪米特法则。 ...

May 27, 2019 · 5 min · jiezi

面向对象基本原则2-里式代换原则与依赖倒置原则

面向对象基本原则(2)- 里式代换原则与依赖倒置原则三、里式代换原则0. 继承的优缺点在面向对象的语言中,继承是必不可少的、非常优秀的语言机制,它有如下优点: 代码共享,减少创建类的工作量,每个子类都拥有父类的方法和属性。提高代码的重用性。子类可以形似父类,但又异于父类,“龙生龙,凤生凤,老鼠生来会打洞”是说子拥有父的“种”,“世界上没有两片完全相同的叶子”是指明子与父的不同。提高代码的可扩展性,实现父类的方法就可以“为所欲为”了,君不见很多开源框架的扩展接口都是通过继承父类来完成的;提高产品或项目的开放性。自然界的所有事物都是优点和缺点并存的,继承的缺点如下: 继承是侵入性的。只要继承,就必须拥有父类的所有属性和方法。降低代码的灵活性。子类必须拥有父类的属性和方法,让子类自由的世界中多了些约束。增强了耦合性。当父类的常量、变量和方法被修改时,需要考虑子类的修改,而且在缺乏规范的环境下,这种修改可能带来非常糟糕的结果——大段的代码需要重构。1. 里式代换原则简介里式代换原则的英文名称是 Liskov Substitution Principle,简称LSP。 里式代换原则的英文定义是: Functions that use pointers or references to base classes must be able to use objects of derived classes without knowing it.意思是:所有引用基类的地方必须能透明地使用其子类的对象。 通俗点讲,就是只要父类能出现的地方子类就可以出现,而且替换为子类也不会产生任何错误或异常,使用者可能根本就不需要知道是父类还是子类。但是,反过来就不行了,有子类出现的地方,父类未必就能适应。 2. 里氏替换原则为良好的继承定义了规范子类必须完全实现父类的方法/** * 枪支抽象类 * Class AbstractGun */abstract class AbstractGun{ public abstract function shoot();}/** * 手枪 * Class Handgun */class Handgun extends AbstractGun{ public function shoot() { echo "手枪射击\n"; }}/** * 步枪 * Class Rifle */class Rifle extends AbstractGun{ public function shoot() { echo "步枪射击\n"; }}如果子类不能完整地实现父类的方法,或者父类的某些方法在子类中已经发生“畸变”,则建议断开父子继承关系,采用依赖、聚合、组合等关系代替继承。 ...

May 25, 2019 · 3 min · jiezi

面向对象基本原则1-单一职责原则与接口隔离原则

面向对象基本原则(1)- 单一职责原则与接口隔离原则一、单一职责原则1. 单一职责原则简介单一职责原则的英文名称是 Single Responsibility Principle,简称SRP。 单一职责原则的原话解释是: There should never be more than one reason for a class to change.意思是:应该有且仅有一个原因引起类的变更。 2. 单一职责原则优点类的复杂性降低,实现什么职责都有清晰明确的定义。可读性提高,复杂性降低,那当然可读性提高了。可维护性提高,可读性提高,那当然更容易维护了。变更引起的风险降低,变更是必不可少的,如果接口的单一职责做得好,一个接口修改只对相应的实现类有影响,对其他的接口无影响,这对系统的扩展性、维护性都有非常大的帮助。3. 最佳实践接口一定要做到单一职责,类的设计尽量做到只有一个原因引起变化。 注意 单一职责原则提出了一个编写程序的标准,用“职责”或“变化原因”来衡量接口或类设计得是否优良,但是“职责”和“变化原因”都是不可度量的,因项目而异,因环境而异。4. Show me the code代码使用PHP7.2语法编写用户业务场景IUserBo 接口负责用户属性 interface IUserBo{ public function setUserID(string $userID); public function getUserID() : string ; public function setPassword(string $password); public function getPassword() : string ; public function setUserName(string $userName); public function getUserName() : string ;}IUserBiz 接口负责用户行为 interface IUserBiz{ public function changePassword(string $password) : bool ; public function deleteUser(IUserBo $userBo) : bool ; public function mapUser(IUserBo $userBo); public function addOrg(IUserBo $userBo, int $orgID) : bool; /** * 给用户添加角色 * @param IUserBo $userBo * @param int $roleID * @return bool */ public function addRole(IUserBo $userBo, int $roleID) : bool ;}UserInfo 类实现 IUserBo, IUserBiz 两个接口 ...

May 23, 2019 · 2 min · jiezi

JavaScript基础学习面向对象部分属性类型

前言JavaScript发明之始,从技术上来讲就是一门面向对象的语言,但在ES6之前,JS的很多特性和传统的面向对象语言有所不同,比如没有类的概念(ES6有了class)。今天结合《JS高编》第六章开始回顾和深入学习面向对象部分,包括对象、原型、原型链、继承等部分。 一、理解对象谈JS的对象之前,先复习一下面向对象的基础概念和特点吧。面向对象OOP(Object-oriented programming),结合维基百科和百度百科的阐述,再谈谈我的理解。 官方解释:面向对象就是基于对象概念,以对象为中心,以类和继承为构造机制,来认识、理解、刻画客观世界和设计、构建相应的软件系统 我的理解:在JavaScript的世界中,万物皆对象。任何事和物你都可以将其定义为一个对象,程序员界有个笑话就是单身狗可以new一个对象嘛......我的粗浅理解,如果我是一个上帝,这个世界的任何人和事相对于我而言都是一个对象。有了控制对象的权力,我就可以对他们进行任何操作。针对事,我可以发布一个号令,发布一个政策,告诉别人怎么执行,什么时候开始,什么时候结束。针对人,我可以把他们分为男人、女人,这就是类。然后我可以限制他们的儿子是男人还是女人,是男人那就必须有和爸爸一样的性别特征,这就是继承。我还可以控制他们什么时间做什么事等等,整个过程我都是围绕某个对象来展开的,那么这个过程叫做面向对象。 特点:1.类2.继承3.封装4.多态具体的在后面学习和复习时再谈。 二、对象的属性类型1.数据属性:[[Configurable]],[[Enumerable]],[[Writable]],[[Value]]2.访问器属性:[[Configurable]],[[Enumerable]],[[Get]],[[Set]] 书上讲到属性类型时,只是简单提了一下是为了表示对象的特性,描述了属性的特征,并且在JS中不能直接访问。光看介绍不太理解到底是干什么的,但是看了数据属性的内容之后,发现不难理解。 我的理解,数据属性就是我们可以从根源去控制一个对象的属性是否能被修改、删除、循环等,并可以通过访问器属性在别人不知道的情况下进行数据处理。通过Object.defineProperty()这个方法,我们可以去设置这些限制对象属性操作的值,从而限制别人对某个对象属性的操作。举个例子,上面的obj这个对象的name属性的值是“勾鑫宇”,从现在起我不想任何人能够修改它的值,那么我就通过数据属性来将这个属性设置为不可修改,别人用obj.name = "张三"来修改就不会生效了。而我如果想在修改name属性的值后同时让age也跟着改变,那么此时就可以用访问器属性来进行数据处理。 我们是通过Object.defineProperty()这个方法来进行两种属性的设置。那么首先了解一下Object.defineProperty()这个方法,它接收三个参数: Object.defineProperty(对象名,属性名,描述符对象)//举例Object.defineProperty(obj,"name",{ writable:false,//设置不可修改 enumerable:false//设置不可循环到该属性})可以在对象的constructor中找到该方法 同时,我们可以通过Object.getOwnPropertyDescriptor()方法来查看这四个特性的设置情况。接受两个参数: Object.getOwnPropertyDescriptor(对象名,属性名)数据属性数据属性包含一个数据值的位置。在这个位置可以读取和写入值,有4个描述其行为的特性。 下面就具体来对每个数据属性进行分析:1.[[Writable]]:英文意思译为“可写的”,可理解为“可修改的”。这个属性用来设置对象的某个属性是否能被修改,默认为true。 //举例let obj = { name:"勾鑫宇", age:23}Object.defineProperty(obj,"name",{ writable:false,//设置不可修改})//这时再进行修改就不会生效,严格模式下会报错obj.name = "张三"console.log(obj.name)//输出的还是勾鑫宇严格模式报错 2.[[Enumerable]]:英文译为“可数的,可枚举的”,是否支持for-in循环来返回属性,默认为true。 //举例let obj = { name:"勾鑫宇", age:23, gender:male}Object.defineProperty(obj,"name",{ enumerable:false,//设置不可通过for-in循环返回})//循环测试for(let i in obj){ console.log(i)//输出结果为age,gender,没有name属性,效果就像隐藏了这个属性。}//但这时我们的name属性还是存在的console.log(obj)3.[[value]]:这个就不说翻译了,大家都知道,就是值。这个特性是设置我们对象某个属性的值,读值、写值都在这里,默认值为undefined。 //举例let obj = { name:"勾鑫宇", age:23, gender:male}Object.defineProperty(obj,"name",{ value:"张三",//设置name的值为张三})console.log(obj.name)//输出为张三//设置value不影响后面再次修改值,value相当于修改了一次你最先定义的值而已。obj.name = "傻逼"concole.log(obj.name)//输出为“傻逼”4.[[Configurable]]:英文译为“可配置的”,这个和前面的Writable有什么区别呢?放到最后讲是有原因的。前面有设置修改,设置循环,设置值,但是还没有设置是否可删除。Configurable就是做这个事情的。它表示能否通过delete删除属性从而重新定义属性,默认值为true。 //举例let obj = { name:"勾鑫宇", age:23, gender:male}Object.defineProperty(obj,"name",{ configurable:false,//不允许删除属性})delete obj.name//报错"Uncaught TypeError: Cannot delete property 'name' of #<Object>"这个属性还有最重要的一个特点,就是当你设置为false过后,就不能再设为true了,即使你设置了也无效。书上说得个时候你再设置value,enumerable都不会生效,只能设置writable,那么我们来试试。 ...

April 28, 2019 · 1 min · jiezi

Python中什么是面向对象封装

文字有点长,对于不想看文字的朋友,可以去这里看视频,内容和这个文字一样的,视频可能更好理解https://www.piqizhu.com/v/yjB...回顾面向过程前面我们已经学过了 面向过程 我们知道,面向过程 的时候,我们的关注点是 步骤 面向过程做事,就是把 一件事情 分割为多个步骤, 然后依次去完成每一个步骤 这样做事可以让我们的事情变的很明朗,不会弄乱 那么, 既然有了面向过程,为什么还要跑出来一个面向对象呢?面向对象是什么东西呢?有啥好处?他和面向过程有啥关系呢? 他们两个我们应该选谁呢?带着这么3个问题,开始本节课的讲解 面向对象概述所谓面向对象, 意思就是,我们的关注点 是对象, 而非过程(步骤) 那么,这里的对象是啥意思呢? 要回答这个问题, 就必须先回到实际的案例中去讲解 上节课的我们的案例是制作一个玩具鸭子,我们的关注点是制作鸭子的每一个步骤 如果我们只是捏个泥娃娃,或者制作一个简单的玩具鸭子, 使用面向过程,是没有多大问题的 但是如果我们面对的一个复杂的事情呢? 有一家玩具公司,这家玩具公司不止生成玩具鸭,还生产玩具狗,玩具猫,玩具猫头鹰, 。。。。。 等等 100多种玩具 如果按照之前的 面向过程思路,那么我们的代码 会很长, 很杂乱, 那 怎么办呢? 这时候就需要使用面向对象的思路来解决问题了 面向对象-封装于是某一天,,这家玩具公司有了一台鸭子制造机器, 这台机器,当我们按下开关后,它就会立刻开始制作玩具鸭子, 此刻,我们不再去关注先做脚,还是先做头, 还是身体, 这台机器会帮我们搞定所有步骤, 我们只需要在机器的出口处,等着完整的玩具鸭子出来就可以了 这时候,我们的关注点,就是这台机器本身,而不是制作鸭子的某一个步骤 这台机器,此刻就是一个对象(整体),此刻,我们就开始了面向对象 可能到这里大家还是不明白,还是有点糊涂,这是正常的, 请允许我再来解释解释 这台鸭子制造机器,包含了 以前制造鸭子的所有步骤,它把制作鸭子的步骤,封装在了机器内部, 留给我们的,只有一个开关,我们只需要按开关,就可以开始制造鸭子 而以前的面向过程,我们需要关注制造鸭子的细节,需要先制作鸭头,然后制作翅膀,然后.... 但是,当我们有了一台封装了详细步骤的机器,只需要关心什么时候按开关,别的都不用关心 这就是面向对象的第一个特性(好处): 封装 封装特性,可以把复杂的信息,流程,包起来,内部处理, 让使用者不去关注细节, 只关心什么时候按开关, 如此一来当我们要制作鸭子的时候,只要按开关就可以了,是不是省心很多?? 再来举个例子比如,某一天,你以程序员的身份,去某家公司工作,老板让你开发一个网站, 此刻,老板就是面向了对象,这里的对象,在老板眼里 就是你, 因为老板只要把任务丢给你,他不关注你用什么电脑写代码,也不关注你用什么输入法,不会关注你写代码的时候听什么歌,不会关注你写代码的时候是穿拖鞋好,还是光脚丫好, 更加不会关注你今天穿什么颜色的内裤 写代码效率更高; 但是在你自己的角度,你就是面向过程的,你会关心自己用哪个电脑写代码更舒服, 你会关注自己用哪个输入法效率更高,你还会关注写代码应该听什么歌.. 等等 而,当你写代码的时候,你使用的电脑,对于你而言,也是一个封装好的对象, 当你在键盘上按下字母A, 你不会关注电脑内部究竟发生了 多么复杂的化学反应, 你只关注,我按了键盘上的字母A,电脑就要显示一个A在屏幕上 ...

April 23, 2019 · 1 min · jiezi

作用域链、垃圾回收机制、闭包及其应用(oop)

执行环境、变量对象 / 活动对象、作用域链执行环境(executioncontext,为简单起见,有时也称为“环境”)是JavaScript中最为重要的一个概念。执行环境定义了变量或函数有权访问的其他数据,决定了它们各自的行为。每个执行环境都有一个与之关联的变量对象(variableobject),环境中定义的所有变量和函数都保存在这个对象中。虽然我们编写的代码无法访问这个对象,但解析器在处理数据时会在后台使用它。全局执行环境是最外围的一个执行环境。根据ECMAScript实现所在的宿主环境不同,表示执行环境的对象也不一样。在Web浏览器中,全局执行环境被认为是window对象,因此所有全局变量和函数都是作为window对象的属性和方法创建的。某个执行环境中的所有代码执行完毕后,该环境被销毁,保存在其中的所有变量和函数定义也随之销毁(全局执行环境直到应用程序退出——例如关闭网页或浏览器——时才会被销毁)。每个函数都有自己的执行环境。当执行流进入一个函数时,函数的环境就会被推入一个环境栈中。而在函数执行之后,栈将其环境弹出,把控制权返回返回给之前的执行环境。ECMAScript程序中的执行流正是由这个方便的机制控制着。当代码在一个环境中执行时,会创建变量对象的一个作用域链(scopechain)。作用域链的用途,是保证对执行环境有权访问的所有变量和函数的有序访问。作用域链的前端,始终都是当前执行的代码所在环境的变量对象。如果这个环境是函数,则将其活动对象(activationobject)作为变量对象。活动对象在最开始时只包含一个变量,即arguments对象(这个对象在全局环境中是不存在的)。作用域链中的下一个变量对象来自包含(外部)环境,而再下一个变量对象则来自下一个包含环境。这样,一直延续到全局执行环境;全局执行环境的变量对象始终都是作用域链中的最后一个对象。标识符解析是沿着作用域链一级一级地搜索标识符的过程。搜索过程始终从作用域链的前端开始,然后逐级地向后回溯,直至找到标识符为止(如果找不到标识符,通常会导致错误发生)。—- 摘自 JavaScript高级程序设计理论说完,直接上代码。function Fn() { var count = 0 function innerFn() { count ++ console.log(‘inner’, count) } return innerFn}var fn = Fn()document.querySelector(’#btn’).addEventListener(‘click’, ()=> { fn() Fn()()})1、 浏览器打开,进入全局执行环境,也就是window对象,对应的变量对象就是全局变量对象。在全局变量对象里定义了两个变量:Fn和fn。2、当代码执行到fn的赋值时,执行流进入Fn函数,Fn的执行环境被创建并推入环境栈,与之对应的变量对象也被创建,当Fn的代码在执行环境中执行时,会创建变量对象的一个作用域链,这个作用域链首先可以访问本地的变量对象(当前执行的代码所在环境的变量对象),往上可以访问来自包含环境的变量对象,如此一层层往上直到全局环境。Fn的变量对象里有两个变量:count和innerFn,其实还有arguments和this,这里先忽略。然后函数返回了innerFn函数出去赋给了fn。3、手动执行点击事件。首先,执行流进入了fn函数,实际上是进入了innerFn函数,innerFn的执行环境被创建并推入环境栈,执行innerFn代码,通过作用域链对Fn的活动对象中的count进行了+1,并且打印。执行完毕,环境出栈。然后,执行流进入了Fn函数,Fn的执行跟第2步的一样,返回了innerFn。接着执行了innerFn函数,innerFn的执行跟前面的一样。每一次点击都执行了fn, Fn, innerFn,而fn和innerFn其实是一样逻辑的函数,但控制台打印出来的结果却有所不同。点击了3次的结果,接下来进入闭包环节。闭包垃圾回收机制先介绍下垃圾回收机制。离开作用域的值将被自动标记为可以回收,因此将在垃圾收集期间被删除。“标记清除”是目前主流的垃圾收集算法,这种算法的思想是给当前不使用的值加上标记,然后再回收其内存。—- 摘自 JavaScript高级程序设计通俗点说就是:1、函数执行完了,其执行环境会出栈,其变量对象自然就离开了作用域,面临着被销毁的命运。但是如果其中的某个变量被其他作用域引用着,那么这个变量将继续保持在内存当中。2、全局变量对象在浏览器关闭时才会被销毁。接下来看看上面的代码。对了先画张图。现在就解释下为什么会有不同的结果。Fn()() — 执行Fn函数,return了innerFn函数并立即执行了innerFn函数,因为innerFn函数引用了Fn变量对象中的count变量,所以即使Fn函数执行完了,count变量还是保留在内存中。等innerFn执行完了,引用也随之消失,此时count变量被回收。所以每次运行Fn()(),count变量的值都是1。fn() — 从fn的赋值开始说起,Fn函数执行后return了innerFn函数赋值给了fn。从这个时候开始Fn的变量对象中的count变量就被innerFn引用着,而innerFn被fn引用着,被引用的都存在于内存中。然后执行了fn函数,实际上执行了存在于内存中的innerFn函数,存在于内存中的count++。执行完成后,innerFn还是被fn引用着,由于fn是全局变量除了浏览器关闭外不会被销毁,以至于这个innerFn函数没有被销毁,再延申就是innerFn引用的count变量也不会被销毁。所以每次运行fn函数实际上执行的还是那个存在于内存中的innerFn函数,自然引用的也是那个存在于内存中的count变量。不像Fn()(),每次的执行实际上都开辟了一个新的内存空间,执行的也是新的Fn函数和innerFn函数。闭包的用途1、通过作用域访问外层函数的私有变量/方法,并且使这些私有变量/方法保留再内存中2、避免全局变量的污染3、代码模块化 / 面向对象编程oop举个例子function Animal() { var hobbies = [] return { addHobby: name => {hobbies.push(name)}, showHobbies: () => {console.log(hobbies)} }}var dog = Animal()dog.addHobby(’eat’)dog.addHobby(‘sleep’)dog.showHobbies()定义了一个Animal的方法,里面有一个私有变量hobbies,这个私有变量外部无法访问。全局定义了dog的变量,并且把Animal执行后的对象赋值给了dog(其实dog就是Animal的实例化对象),通过dog对象里的方法就可以访问Animal中的私有属性hobbies。这么做可以保证私有属性只能被其实例化对象访问,并且一直保留在内存中。当然还可以实例化多个对象,每个实例对象所引用的私有属性也互不相干。当然还可以写成构造函数(类)的方式function Animal() { var hobbies = [] this.addHobby = name => {hobbies.push(name)}, this.showHobbies = () => {console.log(hobbies)}}var dog = new Animal() ...

March 19, 2019 · 1 min · jiezi

Web 开发学习笔记(5) --- 抽象出 Page 类

回顾通过前几篇文章的内容, 我们已经搭建了基于 Flask 框架的一个简单的 Web 应用, server.py 的代码如下from flask import Flask, make_responsefrom flask.views import MethodViewapp = Flask(name)class IndexHandler(MethodView): def get(self): resp = make_response(‘It is a GET request’) resp.headers[‘Strict-Transport-Security’] = ‘max-age=15768000; includeSubDomains; preload’ return respif name == ‘main’: app.add_url_rule(’/’, view_func=IndexHandler.as_view(‘index’)) context = (’./server.cer’, ‘./server.key’) app.run(port=443, host=‘0.0.0.0’, debug=True, threaded=True, ssl_context=context)抽象出 Page 类由于我们在以后的每个页面的 Handler 中都要用到 HSTS, 以及其他一些共用的特性, 我们将其提取出来, 抽象成一个 Page 类, 修改 server.py 如下from flask import Flask, make_responsefrom flask.views import MethodViewapp = Flask(name)class Page(MethodView): def render(self, resp): resp.headers[‘Strict-Transport-Security’] = ‘max-age=15768000; includeSubDomains; preload’ return respclass IndexHandler(Page): def get(self): content = ‘It is a GET request’ return self.render(content)if name == ‘main’: app.add_url_rule(’/’, view_func=IndexHandler.as_view(‘index’)) context = (’./server.cer’, ‘./server.key’) app.run(port=443, host=‘0.0.0.0’, debug=True, threaded=True, ssl_context=context)提取其他共用特性get_args()args 一般出现在 GET 请求中, 比如 Google 搜索时地址栏出现的 https://www.google.com/search?q=awesome, 其中 ? 后面的内容就是 args, 以 key-value 的形式出现. 对应刚才的例子, key 就是 q, value 就是 awesome. 如果有多组 key-value, 中间就用 & 连接, 如 ?q=awesome&type=server. 这种形式很容易让我们联想到 Python 的字典结构 dict. 根据 文档, Flask 是通过 ImmutableMultiDict 来存储 args 的. 我们可以在 Page 类中增加 get_args() 方法from flask import requestclass Page(MethodView): def get_args(self, key): return request.args.get(key)这样, 我们就可以通过调用 get_args() 来获取用户请求中的参数了, 举个栗子class AwesomeHandler(Page): def get(self): if self.get_args(‘q’) == ‘awesome’: # do something return ‘This is an awesome page!‘同样地, 我们可以在 Page 类中增加其他特性, 如 get_date(), get_referer(), get_cookies() 等等, 编写好的 Page 类如下from flask import requestfrom datetime import datetime, dateclass Page(MethodView): def render(self, resp): resp.headers[‘Strict-Transport-Security’] = ‘max-age=15768000; includeSubDomains; preload’ return resp def get_args(self, key): return request.args.get(key) def get_date(self, year=0, month=0, day=0): if year and month and day: return date(year, month, day) else: return date.today() def get_referer(self): return request.headers.get(‘referer’) def get_cookies(self): return request.cookies参考资料http://flask.pocoo.org/docs/1… ...

February 16, 2019 · 2 min · jiezi

PHP面试常考内容之面向对象(3)

PHP面试专栏正式起更,每周一、三、五更新,提供最好最优质的PHP面试内容。继上一篇“PHP面试常考内容之面向对象(2)”发表后,今天更新面向对象的最后一篇(3)。需要(1),(2)的可以直接点文字跳转获取。PHP面试常考内容之面向对象(1)整个面向对象文章的结构涉及的内容模块有:一、面向对象与面向过程有什么区别?二、面向对象有什么特征?三、什么是构造函数和析构函数?四、面向对象的作用域范围有哪几种?五、PHP 中魔术方法有哪些?六、什么是对象克隆?七、this、self和parent的区别是什么?八、抽象类与接口有什么区别与联系?九、PHP面向对象的常考面试题讲解关于PHP面向对象的内容将会被分为三篇文章进行讲解完整块内容,第一篇主要讲解一到四点内容,第二篇主要讲解五到八的内容,第三篇围绕第九点进行讲解。以下正文的内容都来自《PHP程序员面试笔试真题解析》书籍,如果转载请保留出处:九、PHP面向对象的常考面试题讲解【真题1】 什么是面向对象?其主要特征是什么?答案:面向对象是程序的一种设计方式,它是一种对现实世界的理解和抽象的方法,它可以提高程序的重用性,让程序结构更加清晰。面向对象的主要特征为:封装、继承、多态。【真题2】 可以获得对象的类名的函数是( )。A.get_class_name B.get_class C.class_exists D.get_class_vars答案:B。PHP中获取对象的类名函数是get_class()。所以,选项B正确。对于选项A,不存在该方法。所以,选项A错误。对于选项C,class_exists()函数可以检查类是否存在。所以,选项C错误。对于选项D,get_class_vars()函数可以获取类的默认属性。所以,选项D错误。所以,本题的答案是B。【真题3】 请简单说明PHP的垃圾收集机制。答案:在PHP中,当没有任何变量指向该对象时,该对象变为垃圾将会在内存中被销毁,可以防止内存溢出。内存中对变量有引用计数,当计数到0时变量被销毁。【真题4】多态与方法重写有什么关系?答案:多态是指一个类可以被多个类继承,每个子类都可以对父类方法进行重写,每个类里的同名方法可以实现不同的功能从而表现出多种形态,它增强了软件的灵活性和重用性。重写是子类对父类中的方法进行改写。它们的关系是重写让类具备多态性。【真题5】面向对象几大原则是什么?答案:面向对象存在五大基本原则,分别是:单一职责原则、开放封闭原则、替换原则、依赖原则、接口分离原则等。(1)单一职责原则所谓单一职责原则,即一个类最好只做一件事。为了提高内聚性减少引起变化,单一原则是低耦合、高内聚的面向原则上的引申。(2)开放封闭原则软件的功能应该是可扩展的,尽可能减少修改,因为修改程序,可能会对原来的程序造成影响。虽然建议尽可能不修改程序,但是允许通过添加功能来减少修改。(3)替换原则只有子类能够替换基类,在继承机制的约束规范中,子类替换基类时,可以保证运行期内识别子类,保证继承复用。(4)依赖倒置原则高层模块不依赖底层模块,二者都依赖于抽象,抽象不依赖于实体,而实体依赖于抽象。模块间的依赖是通过抽象方法发生的,实现类中不发生直接的依赖关系,而依赖关系是通过接口或抽象类产生的。即接口或抽象类不依赖于实现类,而实现类依赖于接口和抽象类。这种依赖倒置原则可以有效地减少类之间的耦合性,提高系统的稳定性,减少并发引起的风险,提高代码的可读性和可维护性。(5)接口隔离原则建议开发使用多个小的、专门的接口,避免使用一个大的总接口。即每一个功能有一个专门的功能接口,需要用到才调用,不需要全部功能汇总到一个接口,这样可以提高代码的灵活性,降低类之间的耦合性,提高稳定性。【真题6】以下有关PHP高级特性的说法中,正确的是( )。A.可以定义一个类去实现预定义接口Iterator,然后就能像访问数组一样访问这个类创建的对象B.spla_utoload_register()提供了一种更加灵活的方式来实现类的自动加载,不再建议使用_autoload()函数C.PHP在对象中调用一个不可访问方法时,invoke()方法会被自动调用D.匿名函数也叫闭包函数,常用作回调函数参数的值,但是不能作为变量的值来使用答案:B。对于选项A,只有ArrayAccess能够提供像访问数组一样访问这个对象的接口,不能定义一个类或预定义接口Iterator去实现这个功能。所以,选项A错误。对于选项B,因为可以通过spla_utoload_register()函数创建autoload函数的队列,按定义顺序逐个执行,比_autoload()函数只可以定义一次使用更方便,所以不建议使用_autoload()函数。所以,选项B正确。对于选项C,_call方法是在创建一个类实例化后就可以直接调用对象使用,当调用的方法不可访问或没有权限访问时,会自动调用_call方法。所以,选项C错误。对于选项D,匿名函数是可以赋值给变量的。所以,选项D错误。所以,本题的答案是B。【真题7】__autoload()函数的工作原理是什么?答案:使用这个魔术函数的基本条件是,类文件的文件名要和类的名字保持一致。当程序执行到实例化某个类时,如果在实例化前没有引入这个类文件,那么就自动执行__autoload()函数。这个函数根据实例化的类名去查找这个类的路径,一旦找到这个类后就会通过执行include或require载入该类,从而保证程序能够继续执行。如果没有找到,那么报错。【真题8】以下关于PHP命名空间的说法中,不正确的是( )。A.访问任意全局类、函数或常量,都可以使用完全限定名称,例如strlen()或Exception或INI_ALLB.关键字 namespace可用来显式访问当前命名空间或子命名空间中的元素,它等价于类中的 this 操作符 C.任意合法的PHP代码都可以包含在命名空间中,但只有三种类型的代码受命名空间的影响,它们是类、函数和常量D.常量__NAMESPACE__的值是当前命名空间名称的字符串。如果是在全局中,那么它不包括任何命名空间中的代码,本身是一个空字符串答案:B。namespace关键字是用来声明命名空间用的,它并不能等价于this操作符的功能。所以,选项B说法不对。所以,本题的答案是B。【真题9】以下代码的运行结果是( )。<?php class Person{ var $name; } $a = new Person(); $a->name = “张三”; $b = $a; $b->name = “李四”; echo $a->name;?>A.张三 B.李四 C.Null D.什么都没有答案:B。首先$a实例化Person类,把张三赋值给类内的变量name,把对象张三的值给了$b,通过$b去修改类内name的值为李四,所以最后输出Person类内的name,输出得到结果李四。所以,选项B正确,选项A、选项C、选项D错误。所以,本题的答案是B。【真题10】下面说法错误的是( )。A.如果一个类的成员前面有访问修饰符private,那么这些成员不能被继承,在类的外部不可见。但如果成员被指定为protected和public,那么可以被继承,在类的外部也是可见的B.PHP5中,final关键字可以禁止继承和重载C.PHP5中,析构函数的名称是__destruct(),并且不能有任何参数D.继承接口的类必须实现接口中声明的所有方法,在PHP中,如果继承接口的类没有实现接口中的方法,那么将会产生一个致命错误答案:A。对于选项A,private修饰的成员是不可以被继承的,protected的成员是可以被继承的,但是在外部不可见,选项A说法错误,所以,选项A正确。对于选项B,final关键字的方法是禁止被继承和重载的,选项B说法正确,所以选项B错误。对于选项C,析构函数不能有参数,选项C说法正确,所以,选项C错误。对于选项D,继承接口的类没有实现接口中的方法是会产生错误的,选项D说法正确,所以,选项D错误。所以,本题的答案是A。自己整理了一篇“个人编程6年的心得——如何学好编程?”的文章,关注公众号:“琉忆编程库”,回复:“学好”,我发给你。【真题11】 定义类成员的访问权限控制符有哪些?默认修饰符是什么?答案:类成员的访问修饰符有public、private、protected,主要用来修饰类中的成员属性和方法。public是公共类型,允许在类的内部或子类中使用,也可以在类外部被访问。private是私有类型,只能在类的内部被使用,不能被继承使用。protected是保护类型,只能在类的内部或子类中使用。如果不使用public、private、protected等关键字修饰方法或属性,那么可以使用var关键字,它的功能等同于public,可以在类内或类外被调用,也可以被继承使用。其中,PHP默认的修饰符是public,即公有类型。类前面只能加final、abstract关键字,被final修饰的属性或方法是不能被继承的,只能在当前类中使用,abstract定义的类或方法,叫作抽象类或抽象方法。属性前面:必须有访问修饰符(private,protected,public,var)。【真题12】 PHP的魔术方法包含哪些(越多越好)?在什么情况下被自动调用?答案:PHP可用的魔术方法会在特定情况下被自动调用,但是前提是特定的条件被触发,并且这些魔术方法可以在类中作为方法。PHP的魔术方法有:1)_construct():构造函数,创建对象时自动被调用。2)_destruct():析构函数,对象的所有引用都被删除或者当对象被显式销毁时执行。3)__clone():克隆函数,调用clone方法时自动调用。4)__set():当程序试图写入一个不存在或不可见的成员变量时自动调用。该函数在类中定义时必须有两个参数:变量名和变量值。5)__get():当程序调用一个未定义或不可见的成员变量时自动调用__get()来读取变量值。定义时必有有一个参数:变量名。6)__call():当程序试图调用不存在或不可见的成员方法时,自动调用__call()。__call()方法一般用于监视错误的方法调用。为了避免当调用的方法不存在时产生错误,可以使用__call()方法来避免。该方法包含两个参数:方法名和方法参数。其中,方法参数以数组形式存在。7)__sleep():使用serialize()实现序列化对象时,先调用该方法,可以用来清除对象并返回一个该对象中所有变量的数组。8)__wakeup():使用unserialize()还原一个被序列化的对象时,先执行该方法,恢复在序列化中可能丢失的数据库连接及相关工作。9)__toString():当使用echo或print输出对象时,将对象转化为字符串。10)__autoload():调用未被实例化的类时,自动调用,在指定路径下查找和该类名称相同的文件。【真题13】 $this、self和parent这三个关键词分别代表什么?在哪些场合下使用?答案:$this表示当前对象,在当前类中可以通过->符调用类内的属性和方法。self表示当前类,只能通过self的形式(“self::方法或属性”)调用类内的方法。parent表示当前类的父类,调用父类内的方法只能使用“parent::”形式调用。【真题14】下面关于面向对象的描述中,错误的是( )。A.父类的构造函数与析构函数不会自动被调用B.成员变量需要用public、protected、private修饰,在定义变量时不再需要var关键字C.父类中定义的静态成员,不可以在子类中直接调用D.包含抽象方法的类必须为抽象类,抽象类不能被实例化答案:A。对于选项A,子类继承父类,如果子类没有构造函数和析构函数,那么实例化子类时会自动调用父类的构造函数和析构函数;但如果子类只有构造函数没有析构函数时,那么实例化子类时,自动调用的是子类的构造函数,销毁对象时调用父类的析构函数;如果子类没有构造函数只有析构函数,那么实例化子类时会自动调用父类的构造函数,销毁对象时调用子类的析构函数,选项A说法不完全。所以,选项A正确。对于选项B,成员变量使用了public、protected、private修饰定义变量时是不需要var关键字的,选项B说法正确。所以,选项B错误。对于选项C,父类中的静态成员,子类中是不可以直接访问的,选项B说法正确。所以,选项C错误。对于选项D,一个包含抽象方法的类必须是抽象类,并且抽象类不能被实例化。选项D说法正确。所以,选项D错误。所以,本题的答案是A。【真题15】 在PHP中,如果派生类与父类有相同名字的函数,那么派生类的函数会替换父类的函数,有如下程序代码:<?php class A { function disName(){ echo “Picachu”; } } class B extends A { var $tmp=’’; function disName(){ echo “Doraemon”; } } $cartoon = new B; $cartoon->disName();?>上述代码的运行结果为( )。A.tmp B.Picachu C.disName D.Doraemon E.无输出答案:D。当派生类继承父类时,如果通过实例化一个派生类的对象来访问对象的方法时,派生类不存在父类中的方法,那么执行父类中的方法。如果派生类和父类存在相同名字的方法,那么派生类的方法会覆盖父类方法,执行派生类的方法。所以,本题中可以执行派生类的disName()方法。所以,选项D正确,选项A、选项B、选项C、选项E错误。所以,本题的答案是D。【真题16】什么是抽象类和接口?抽象类和接口有什么不同和相似的地方?答案:被关键字abstract修饰的类叫作抽象类,抽象类是不能被实例化的。被abstract修饰的方法为抽象方法,一个类只要有一个抽象方法,这个类一定是抽象类。接口是通过关键字interface来定义的,可以指定某个类必须实现哪些方法,但不需要定义这些方法的具体实现。PHP类只支持是单重继承的,但通过接口可以实现PHP类的多重继承。抽象类和接口的不同和相似的地方如下所示。1)抽象类是一种不能被实例化的类,只能作为其他类的父类来使用。2)抽象类是通过关键字abstract来声明的。3)抽象类与普通类相似,都包含成员变量和成员方法,两者的区别在于,抽象类中至少要包含一个抽象方法。4)抽象方法没有方法体,该方法就是要被子类重写的。5)抽象方法的格式为:abstract function abstractMethod()。6)因为PHP中只支持单重继承,所以如果想实现多重继承,那么就要使用接口。也就是说,子类可以实现多个接口。7)接口类是通过interface关键字来声明的,接口类中的成员变量和方法都是public的,可以不用显式地使用public来修饰。8)接口中的方法没有方法体。接口中的方法就是要被子类继承实现的。9)子类继承抽象类使用extends关键字,子类实现接口使用implements关键字。【真题17】用类编程实现:Stu类中有两个私有属性name和sex,有两个公有方法,setName()和setSex()参数自定,方法可实现对两个私有属性进行修改。在实例化类时要求对私有属性能初始化。答案:实现代码如下:<?php Class Stu{ private $name; private $sex; public function setName($name){ $this->name = $name; } public function setSex($sex){ $this->sex = $sex; } }?>【真题18】 假如有一个类Person,实例化(new)一个对象$p,那么以下使用对象$p调用Person类中的getInfo方法的写法中,正确的是( )。A.$p=>getInfo(); B.$this->getInfo(); C.$p->getInfo(); D.$p::getInfo();参考答案:C。分析:“::”主要用于访问类中的静态成员,“->”主要用于访问类中的变量和方法,“=>”主要应用在数组中的key和value映射时使用。所以,选项A、选项B、选项D错误,选项C正确。【真题19】php中public、protected、private三种访问控制模式的区别是什么?参考答案:php中public、protected、private三种访问控制模式的区别如下:访 问 模 式 描 述public 共有,任何地方都可以访问protected 继承,只能在本类或子类中访问,在其他地方不能使用private 私有,只能在本类中访问,在其他地方不能使用【真题20】 在PHP面向对象中,下面关于final修饰符的描述中,错误的是( )。A.使用final标识的类不能被继承 B.在类中使用final标识的成员方法,在子类中不能被覆盖C.不能使用final标识成员属性 D.使用final标识的成员属性,不能在子类中再次定义参考答案:D。分析:因为final只能修饰类与方法,不能修饰类的属性。所以,选项D错误。PS:由于真题较多,仅罗列PHP面向对象笔试中经常遇到的20道考题!至此本周(2019-2-11 至 2019-2-15 的面向对象专题已更新完毕,以上的内容只是摘取了PHP面向对象中最常考的内容,个别内容没有罗列可以从原书中获取。)感谢大家的支持!预告:下周(2019-2.18 —— 2.22)更新“PHP面试常考内容之Memcache和Redis缓存的”专题,敬请期待。以上内容摘自《PHP程序员面试笔试真题解析》书籍,该书已在天猫、京东、当当等电商平台销售。更多PHP相关的面试知识、考题可以关注公众号获取:琉忆编程库对本文有什么问题或建议都可以进行留言,将不断完善追求极致,感谢你们的支持。 ...

February 14, 2019 · 1 min · jiezi

PHP面试常考内容之面向对象(2)

PHP面试专栏正式起更,每周一、三、五更新,提供最好最优质的PHP面试内容。继上一篇“PHP面试常考内容之面向对象(1)”发表后,今天更新(2),需要(1)的可以直接点击文字进行跳转获取。整个面向对象文章的结构涉及的内容模块有:一、面向对象与面向过程有什么区别?二、面向对象有什么特征?三、什么是构造函数和析构函数?四、面向对象的作用域范围有哪几种?五、PHP 中魔术方法有哪些?六、什么是对象克隆?七、this、self和parent的区别是什么?八、抽象类与接口有什么区别与联系?九、PHP面向对象的常考面试题讲解关于PHP面向对象的内容将会被分为三篇文章进行讲解完整块内容,第一篇主要讲解一到四点内容,第二篇主要讲解五到八的内容,第三篇围绕第九点进行讲解。以下正文的内容都来自《PHP程序员面试笔试宝典》书籍,如果转载请保留出处:五、PHP种魔术方法有哪些?在PHP中,把所有以__(两个下画线)开头的类方法保留为魔术方法。所以在定义类方法时,不建议使用 __ 作为方法的前缀。下面分别介绍每个魔术方法的作用。1.__get、__set、__isset、__unset这四个方法是为在类和它们的父类中没有声明的属性而设计的。1)在访问类属性的时候,若属性可以访问,则直接返回;若不可以被访问,则调用__get 函数。方法签名为:public mixed __get ( string $name )2)在设置一个对象的属性时,若属性可以访问,则直接赋值;若不可以被访问,则调用__set 函数。方法签名为:public void __set ( string $name , mixed $value )3)当对不可访问的属性调用 isset() 或 empty() 时,__isset() 会被调用。方法签名为:public bool __isset ( string $name )4)当对不可访问属性调用 unset() 时,__unset() 会被调用。方法签名为:public bool _unset ( string $name )需要注意的是,以上存在的不可访问包括属性没有定义,或者属性的访问控制为proteced或private(没有访问权限的属性)。下面通过一个例子把对象变量保存在另外一个数组中。<?php class Test { /* 保存未定义的对象变量 */ private $data = array(); public function __set($name, $value){ $this->data[$name] = $value; } public function __get($name){ if(array_key_exists($name, $this->data)) return $this->data[$name]; return NULL; } public function __isset($name){ return isset($this->data[$name]); } public function __unset($name){ unset($this->data[$name]); } } $obj = new Test; $obj->a = 1; echo $obj->a . “\n”;?>程序的运行结果为12.__construct、__destruct1)__construct 构造函数,实例化对象时被调用。2)__destruct 析构函数,当对象被销毁时调用。通常情况下,PHP只会释放对象所占有的内存和相关的资源,对于程序员自己申请的资源,需要显式地去释放。通常可以把需要释放资源的操作放在析构方法中,这样可以保证在对象被释放的时候,程序员自己申请的资源也能被释放。例如,可以在构造函数中打开一个文件,然后在析构函数中关闭文件。<?php class Test { protected $file = NULL; function __construct(){ $this->file = fopen(“test”,“r”); } function __destruct(){ fclose($this->file); } }?>3.__call()和__callStatic()1)__call( $method, $arg_array ):当调用一个不可访问的方法时会调用这个方法。2)__callStatic的工作方式与 __call() 类似,当调用的静态方法不存在或权限不足时,会自动调用__callStatic()。使用示例如下: <?php class Test { public function __call ($name, $arguments) { echo “调用对象方法 ‘$name’ “. implode(’, ‘, $arguments). “\n”; } public static function __callStatic ($name, $arguments) { echo “调用静态方法 ‘$name’ “. implode(’, ‘, $arguments). “\n”; } } $obj = new Test; $obj->method1(‘参数1’); Test::method2(‘参数2’); ?>程序的运行结果为调用对象方法 ‘method1’ 参数1 调用静态方法 ‘method2’ 参数24.__sleep()和__wakeup()1)__sleep 串行化的时候调用。2)__wakeup 反串行化的时候调用。也就是说,在执行serialize()和unserialize()时,会先调用这两个函数。例如,在序列化一个对象时,如果这个对象有一个数据库连接,想要在反序列化中恢复这个连接的状态,那么就可以通过重载这两个方法来实现。示例代码如下:<?php class Test { public $conn; private $server, $user, $pwd, $db; public function __construct($server, $user, $pwd, $db) { $this->server = $server; $this->user = $user; $this->pwd = $pwd; $this->db = $db; $this->connect(); } private function connect() { $this->conn = mysql_connect($this->server, $this->user, $this->pwd); mysql_select_db($this->db, $this->conn); } public function __sleep() { return array(‘server’, ‘user’, ‘pwd’, ‘db’); } public function __wakeup() { $this->connect(); } public function __destruct(){ mysql_close($conn); } }?>5.__toString()__toString 在打印一个对象时被调用,可以在这个方法中实现想要打印的对象的信息,使用示例如下:<?php class Test { public $age; public function __toString() { return “age:$this->age”; } } $obj = new Test(); $obj->age=20; echo $obj;?>程序的运行结果为age:206.__invoke()在引入这个魔术方法后,可以把对象名当作方法直接调用,它会间接调用这个方法,使用示例如下:<?php class Test { public function __invoke() { print “hello world”; } } $obj = new Test; $obj();?>程序的运行结果为hello world7.__set_state()调用 var_export 时被调用,用__set_state的返回值作为var_export 的返回值。使用示例如下:<?php class People { public $name; public $age; public static function __set_state ($arr) { $obj = new People; $obj->name = $arr[’name’]; $obj->age = $arr[‘aage’]; return $obj; } } $p = new People; $p->age = 20; $p->name = ‘James’; var_dump(var_export($p));?>程序的运行结果为People::__set_state(array( ’name’ => ‘James’, ‘age’ => 20,)) NULL8.__clone()这个方法在对象克隆的时候被调用,php提供的__clone()方法对一个对象实例进行浅拷贝,也就是说,对对象内的基本数值类型通过值传递完成拷贝,当对象内部有对象成员变量的时候,最好重写__clone方法来实现对这个对象变量的深拷贝。使用示例如下:<?php class People { public $age; public function __toString() { return “age:$this->age \n”; } } class MyCloneable { public $people; function __clone() { $this->people = clone $this->people; //实现对象的深拷贝 } } $obj1 = new MyCloneable(); $obj1->people = new People(); $obj1->people->age=20; $obj2 = clone $obj1; $obj2->people->age=30; echo $obj1->people; echo $obj2->people;?>程序的运行结果为age:20 age:30由此可见,通过对象拷贝后,对其中一个对象值的修改不影响另外一个对象。9.__autoload()当实例化一个对象时,如果对应的类不存在,则该方法被调用。这个方法经常的使用方法为:在方法体中根据类名,找出类文件,然后require_one 导入这个文件。由此,就可以成功地创建对象了,使用示例如下:Test.php:<?php class Test { function hello() { echo ‘Hello world’; } }?>index.php:<?php function __autoload( $class ) { $file = $class . ‘.php’; if ( is_file($file) ) { require_once($file); //导入文件 } } $obj = new Test(); $obj->hello();?>程序的运行结果为Hello world在index.php中,由于没有包含Test.php,在实例化Test对象的时候会自动调用__autoload方法,参数$class的值即为类名Test,这个函数中会把Test.php引进来,由此Test对象可以被正确地实例化。这种方法的缺点是需要在代码中文件路径做硬编码,当修改文件结构的时候,代码也要跟着修改。另一方面,当多个项目之间需要相互引用代码的时候,每个项目中可能都有自己的__autoload,这样会导致两个__autoload冲突。当然可以把__autoload修改成一个。这会导致代码的可扩展性和可维护性降低。由此从PHP5.1开始引入了spl_autoload,可以通过spl_autoload_register注册多个自定义的autoload方法,使用示例如下:index.php<?php function loadprint( $class ) { $file = $class . ‘.php’; if (is_file($file)) { require_once($file); } } spl_autoload_register( ’loadprint’ ); //注册自定义的autoload方法从而避免冲突 $obj = new Test(); $obj->hello();?>spl_autoload是_autoload()的默认实现,它会去include_path中寻找$class_name(.php/.inc) 。除了常用的spl_autoload_register外,还有如下几个方法:1)spl_autoload:_autoload()的默认实现。2)spl_autoload_call:这个方法会尝试调用所有已经注册的__autoload方法来加载请求的类。3)spl_autoload_functions:获取所有被注册的__autoload方法。4)spl_autoload_register:注册__autoload方法。5)spl_autoload_unregister:注销已经注册的__autoload方法。6)spl_autoload_extensions:注册并且返回spl_autoload方法使用的默认文件的扩展名。引申:PHP有哪些魔术常量?除了魔术变量外,PHP还定义了如下几个常用的魔术常量。1)LINE:返回文件中当前的行号。2)FILE:返回当前文件的完整路径。3)FUNCTION:返回所在函数名字。4)CLASS:返回所在类的名字。5)METHOD:返回所在类方法的名称。与__FUNCTION__不同的是,__METHOD__返回的是“class::function”的形式,而__FUNCTION__返回“function”的形式。6)DIR:返回文件所在的目录。如果用在被包括文件中,则返回被包括的文件所在的目录(PHP 5.3.0中新增)。7)NAMESPACE:返回当前命名空间的名称(区分大小写)。此常量是在编译时定义的(PHP 5.3.0 新增)。8)TRAIT:返回 Trait 被定义时的名字。Trait 名包括其被声明的作用区域(PHP 5.4.0 新增)。六、什么是对象克隆?对于对象而言,PHP用的是引用传递,也就是说,对象间的赋值操作只是赋值了一个引用的值,而不是整个对象的内容,下面通过一个例子来说明引用传递存在的问题:<?php class My_Class { public $color; } $obj1 = new My_Class (); $obj1->color = “Red”; $obj2 = $obj1; $obj2->color =“Blue”; //$obj1->color的值也会变成"Blue”?>因为PHP使用的是引用传递,所以在执行$obj2 = $obj1后,$obj1和$obj2都是指向同一个内存区(它们在内存中的关系如下图所示),任何一个对象属性的修改对另外一个对象也是可见的。在很多情况下,希望通过一个对象复制出一个一样的但是独立的对象。PHP提供了clone关键字来实现对象的复制。如下例所示:<?php class My_Class { public $color; } $obj1 = new My_Class (); $obj1->color = “Red”; $obj2 = clone $obj1; $obj2->color =“Blue”; //此时$obj1->color的值仍然为"Red”?>$obj2 = clone $obj1把obj1的整个内存空间复制了一份存放到新的内存空间,并且让obj2指向这个新的内存空间,通过clone克隆后,它们在内存中的关系如下图所示。此时对obj2的修改对obj1是不可见的,因为它们是两个独立的对象。在学习C++的时候有深拷贝和浅拷贝的概念,显然PHP也存在相同的问题,通过clone关键字克隆出来的对象只是对象的一个浅拷贝,当对象中没有引用变量的时候这种方法是可以正常工作的,但是当对象中也存在引用变量的时候,这种拷贝方式就会有问题,下面通过一个例子来进行说明:<?php class My_Class { public $color; } $c =“Red”; $obj1 = new My_Class (); $obj1->color =&$c; //这里用的是引用传递 $obj2 = clone $obj1; //克隆一个新的对象 $obj2->color=“Blue”; //这时,$obj1->color的值也变成了"Blue”?>在这种情况下,这两个对象在内存中的关系如下图所示。从上图中可以看出,虽然obj1与obj2指向的对象占用了独立的内存空间,但是对象的属性color仍然指向一个相同的存储空间,因此当修改了obj2->color的值后,意味着c的值被修改,显然这个修改对obj1也是可见的。这就是一个非常典型的浅拷贝的例子。为了使两个对象完全独立,就需要对对象进行深拷贝。那么如何实现呢,PHP提供了类似于__clone方法(类似于C++的拷贝构造函数)。把需要深拷贝的属性,在这个方法中进行拷贝:使用示例如下:<?php class My_Class { public $color; public function __clone() { $this->color = clone $this->color; } } $c =“Red”; $obj1 = new My_Class (); $obj1->color =&$c; $obj2 = clone $obj1; $obj2->color=“Blue”; //这时,$obj1->color的值仍然为"Red”?>通过深拷贝后,它们在内存中的关系如图1-4所示。通过在__clone方法中对对象的引用变量color进行拷贝,使obj1与obj2完全占用两块独立的存储空间,对obj2的修改对obj1也不可见。自己整理了一篇“如果遇到代码怎么改都没效果,怎么办?”的文章,关注公众号:“琉忆编程库”,回复:“问题”,我发给你。七、this、self和parent的区别是什么?this、self、parent三个关键字从字面上比较好理解,分别是指这、自己、父亲。其中,this指的是指向当前对象的指针(暂用C语言里面的指针来描述),self指的是指向当前类的指针,parent指的是指向父类的指针。以下将具体对这三个关键字进行分析。##1.this关键字## 1 <?php 2 class UserName { 3 private $name; // 定义成员属性 4 function __construct($name) { 5 $this->name = $name; // 这里已经使用了this指针 6 } 7 // 析构函数 8 function __destruct() { 9 } 10 // 打印用户名成员函数 11 function printName() { 12 print ($this->name."\n") ; // 又使用了this指针 13 } 14 } 15 // 实例化对象 16 $nameObject = new UserName ( “heiyeluren” ); 17 // 执行打印 18 $nameObject->printName (); // 输出: heiyeluren 19 // 第二次实例化对象 20 $nameObject2 = new UserName ( “PHP5” ); 21 // 执行打印 22 $nameObject2->printName (); // 输出:PHP5 23 ?>上例中,分别在5行和12行使用了this指针,那么this到底是指向谁呢?其实,this是在实例化的时候来确定指向谁,例如,第一次实例化对象的时候(16行),当时this就是指向$nameObject 对象,那么执行第12行打印的时候就把print($this->name)变成了print ($nameObject->name),输出"heiyeluren"。对于第二个实例化对象,print( $this- >name )变成了print( $nameObject2->name ),于是就输出了"PHP5"。所以,this就是指向当前对象实例的指针,不指向任何其他对象或类。2.self关键字先要明确一点,self是指向类本身,也就是self是不指向任何已经实例化的对象,一般self用来访问类中的静态变量。 1 <?php 2 class Counter { 3 // 定义属性,包括一个静态变量 4 private static $firstCount = 0; 5 private $lastCount; 6 // 构造函数 7 function __construct() { 8 // 使用self来调用静态变量,使用self调用必须使用::(域运算符号) 9 $this->lastCount = ++ selft::$firstCount; 10 } 11 // 打印lastCount数值 12 function printLastCount() { 13 print ($this->lastCount) ; 14 } 15 } 16 // 实例化对象 17 $countObject = new Counter (); 18 $countObject->printLastCount (); // 输出 1 19 ?>上述示例中,在第4行定义了一个静态变量$firstCount,并且初始值为0,那么在第9行的时候调用了这个值,使用的是self来调用,中间使用域运算符“::”来连接,这时候调用的就是类自己定义的静态变量$firstCount,它与下面对象的实例无关,只是与类有关,无法使用this来引用,只能使用 self来引用,因为self是指向类本身,与任何对象实例无关。3.parent关键字parent是指向父类的指针,一般使用parent来调用父类的构造函数。 1 <?php 2 // 基类 3 class Animal { 4 // 基类的属性 5 public $name; // 名字 6 // 基类的构造函数 7 public function __construct($name) { 8 $this->name = $name; 9 } 10 } 11 // 派生类 12 class Person extends Animal // Person类继承了Animal类 13 { 14 public $personSex; // 性别 15 public $personAge; // 年龄 16 // 继承类的构造函数 17 function __construct($personSex, $personAge) { 18 parent::__construct ( “heiyeluren” ); // 使用parent调用了父类的构造函数 19 $this->personSex = $personSex; 20 $this->personAge = $personAge; 21 } 22 function printPerson() { 23 print ($this->name . " is " . $this->personSex . “,this year " . $this->personAge) ; 24 } 25 } 26 // 实例化Person对象 27 $personObject = new Person ( “male”, “21” ); 28 // 执行打印 29 $personObject->printPerson (); // 输出:heiyeluren is male,this year 21 30 ?>上例中,成员属性都是public的,特别是父类的,是为了供继承类通过this来访问。第18行: parent::__construct( “heiyeluren” ),使用了parent来调用父类的构造函数进行对父类的初始化,因为父类的成员都是public的,于是就能够在继承类中直接使用 this来访问从父类继承的属性。八、抽象类与接口有什么区别与联系?抽象类应用的定义如下:abstract class ClassName{}抽象类具有以下特点:1)定义一些方法,子类必须实现父类所有的抽象方法,只有这样,子类才能被实例化,否则子类还是一个抽象类。2)抽象类不能被实例化,它的意义在于被扩展。3)抽象方法不必实现具体的功能,由子类来完成。4)当子类实现抽象类的方法时,这些方法的访问控制可以和父类中的一样,也可以有更高的可见性,但是不能有更低的可见性。例如,某个抽象方法被声明为protected的,那么子类中实现的方法就应该声明为protected或者public的,而不能声明为private。5)如果抽象方法有参数,那么子类的实现也必须有相同的参数个数,必须匹配。但有一个例外:子类可以定义一个可选参数(这个可选参数必须要有默认值),即使父类抽象方法的声明里没有这个参数,两者的声明也无冲突。下面通过一个例子来加深理解:<?php abstract class A{ abstract protected function greet($name); } class B extends A { public function greet($name, $how=“Hello “) { echo $how.$name."\n”; } } $b = new B; $b->greet(“James”); $b->greet(“James”,“Good morning “);?>程序的运行结果为Hello JamesGood morning James定义抽象类时,通常需要遵循以下规则:1)一个类只要含有至少一个抽象方法,就必须声明为抽象类。2)抽象方法不能够含有方法体。接口可以指定某个类必须实现哪些方法,但不需要定义这些方法的具体内容。在PHP中,接口是通过interface关键字来实现的,与定义一个类类似,唯一不同的是接口中定义的方法都是公有的而且方法都没有方法体。接口中所有的方法都是公有的,此外接口中还可以定义常量。接口常量和类常量的使用完全相同,但是不能被子类或子接口所覆盖。要实现一个接口,可以通过关键字implements来完成。实现接口的类中必须实现接口中定义的所有方法。虽然PHP不支持多重继承,但是一个类可以实现多个接口,用逗号来分隔多个接口的名称。下面给出一个接口使用的示例:<?php interface Fruit { const MAX_WEIGHT = 3; //静态常量 function setName($name); function getName(); } class Banana implements Fruit { private $name; function getName() { return $this->name; } function setName($_name) { $this->name = $_name; } } $b = new Banana(); //创建对象 $b->setName(“香蕉”); echo $b->getName(); echo “<br />”; echo Banana::MAX_WEIGHT; //静态常量?>程序的运行结果为香蕉 3接口和抽象类主要有以下区别:抽象类:PHP5支持抽象类和抽象方法。被定义为抽象的类不能被实例化。任何一个类,如果它里面至少有一个方法是被声明为抽象的,那么这个类就必须被声明为抽象的。被定义为抽象的方法只是声明了其调用方法和参数,不能定义其具体的功能实现。抽象类通过关键字abstract来声明。接口:可以指定某个类必须实现哪些方法,但不需要定义这些方法的具体内容。在这种情况下,可以通过interface关键字来定义一个接口,在接口中声明的方法都不能有方法体。二者虽然都是定义了抽象的方法,但是事实上两者区别还是很大的,主要区别如下:1)对接口的实现是通过关键字implements来实现的,而抽象类继承则是使用类继承的关键字extends实现的。2)接口没有数据成员(可以有常量),但是抽象类有数据成员(各种类型的成员变量),抽象类可以实现数据的封装。3)接口没有构造函数,抽象类可以有构造函数。4)接口中的方法都是public类型,而抽象类中的方法可以使用private、protected或public来修饰。5)一个类可以同时实现多个接口,但是只能实现一个抽象类。预告:PHP面试常考内容之面向对象(3)将于本周五(2019.2-15)更新。以上内容摘自《PHP程序员面试笔试宝典》书籍,该书已在天猫、京东、当当等电商平台销售。更多PHP相关的面试知识、考题可以关注公众号获取:琉忆编程库对本文有什么问题或建议都可以进行留言,我将不断完善追求极致,感谢你们的支持。 ...

February 13, 2019 · 5 min · jiezi

PHP面试之面向对象(1)

PHP面试专栏正式起更,每周一、三、五更新,提供最好最优质的PHP面试内容。PHP中面向对象常考的知识点有以下7点,我将会从以下几点进行详细介绍说明,帮助你更好的应对PHP面试常考的面向对象相关的知识点和考题。整个面向对象文章的结构涉及的内容模块有:一、面向对象与面向过程有什么区别?二、面向对象有什么特征?三、什么是构造函数和析构函数?四、面向对象的作用域范围有哪几种?五、PHP 中魔术方法有哪些?六、什么是对象克隆?七、this、self和parent的区别是什么?八、抽象类与接口有什么区别与联系?九、PHP面向对象的常考面试题讲解关于PHP面向对象的内容将会被分为三篇文章进行讲解完整块内容,第一篇主要讲解一到四点内容,第二篇主要讲解五到八的内容,第三篇围绕第九点进行讲解。以下正文的内容都来自《PHP程序员面试笔试宝典》书籍,如果转载请保留出处:一、面向对象与面向过程有什么区别?面向对象是当今软件开发方法的主流方法之一,它是把数据及对数据的操作方法放在一起,作为一个相互依存的整体,即对象。对同类对象抽象出其共性,即类,类中的大多数数据,只能被本类的方法进行处理。类通过一个简单的外部接口与外界发生关系,对象与对象之间通过消息进行通信。程序流程由用户在使用中决定。例如,站在抽象的角度,人类具有身高、体重、年龄、血型等一些特称,人类会劳动、会直立行走、会吃饭、会用自己的头脑去创造工具等这些方法,人类仅仅只是一个抽象的概念,它是不存在的实体,但是所有具备人类这个群体的属性与方法的对象都称为人,这个对象人是实际存在的实体,每个人都是人这个群体的一个对象。而面向过程是一种以事件为中心的开发方法,就是自顶向下顺序执行,逐步求精,其程序结构是按功能划分为若干个基本模块,这些模块形成一个树状结构,各模块之间的关系也比较简单,在功能上相对独立,每一模块内部一般都是由顺序、选择和循环三种基本结构组成,其模块化实现的具体方法是使用子程序,而程序流程在写程序时就已经决定。例如五子棋,面向过程的设计思路就是首先分析问题的步骤:第一步,开始游戏;第二步,黑子先走;第三步,绘制画面;第四步,判断输赢;第五步,轮到白子;第六步,绘制画面;第七步,判断输赢;第八步,返回步骤二;第九步,输出最后结果。把上面每个步骤用分别的函数来实现,就是一个面向过程的开发方法。具体而言,二者主要有以下几个方面的不同之处。1)出发点不同。面向对象是用符合常规思维方式来处理客观世界的问题,强调把问题域的要领直接映射到对象及对象之间的接口上。而面向过程方法则不然,它强调的是过程的抽象化与模块化,它是以过程为中心构造或处理客观世界问题的。2)层次逻辑关系不同。面向对象方法则是用计算机逻辑来模拟客观世界中的物理存在,以对象的集合类作为处理问题的基本单位,尽可能地使计算机世界向客观世界靠拢,以使问题的处理更清晰直接,面向对象方法是用类的层次结构来体现类之间的继承和发展。面向过程方法处理问题的基本单位是能清晰准确地表达过程的模块,用模块的层次结构概括模块或模块间的关系与功能,把客观世界的问题抽象成计算机可以处理的过程。3)数据处理方式与控制程序方式不同。面向对象方法将数据与对应的代码封装成一个整体,原则上其他对象不能直接修改其数据,即对象的修改只能由自身的成员函数完成,控制程序方式上是通过“事件驱动”来激活和运行程序。而面向过程方法是直接通过程序来处理数据,处理完毕后即可显示处理结果,在控制程序方式上是按照设计调用或返回程序,不能自由导航,各模块之间存在着控制与被控制、调用与被调用。4)分析设计与编码转换方式不同。面向对象方法贯穿软件生命周期的分析、设计及编码之间是一种平滑过程,从分析到设计再到编码是采用一致性的模型表示,即实现的是一种无缝连接。而面向过程方法强调分析、设计及编码之间按规则进行转换,贯穿软件生命周期的分析、设计及编码之间,实现的是一种有缝的连接。二、面向对象有什么特征?面向对象的主要特征有抽象、继承、封装和多态。1)抽象。抽象就是忽略一个主题中与当前目标无关的那些方面,以便更充分地注意与当前目标有关的方面。抽象并不打算了解全部问题,而只是选择其中的一部分,暂时不用部分细节。抽象包括两个方面,一是过程抽象,二是数据抽象。2)继承。继承是一种联结类的层次模型,并且允许和鼓励类的重用,它提供了一种明确表述共性的方法。对象的一个新类可以从现有的类中派生,这个过程称为类继承。新类继承了原始类的特性,新类称为原始类的派生类(子类),而原始类称为新类的基类(父类)。派生类可以从它的基类那里继承方法和实例变量,并且子类可以修改或增加新的方法使之更适合特殊的需要。3)封装。封装是指将客观事物抽象成类,每个类对自身的数据和方法实行保护。类可以把自己的数据和方法只让可信的类或者对象操作,对不可信的信息进行隐藏。4)多态。多态是指允许不同类的对象对同一消息做出响应。多态包括参数化多态和包含多态。多态性语言具有灵活、抽象、行为共享、代码共享的优势,很好地解决了应用程序函数同名问题。自己整理了一篇“PHP和MySQL面试中爱考的10道题”的文章,关注公众号:“琉忆编程库”,回复:“10”,我发给你。三、什么是构造函数和析构函数?1.构造函数在PHP5之前的版本,构造函数的名字必须与类的名字相同,而从PHP5开始,开发者可以定义一个名为__construct的方法作为构造函数。构造函数的作用就是当类被实例化的时候会被自动调用,因此构造函数主要用于做一些初始化的工作。使用__construct作为构造函数名字的一个好处是,当类名修改的时候,不需要修改构造函数的名字。它的声明形式为void __construct ([ mixed $args [, $… ]] )在C++语言中,子类的构造函数会隐式地调用父类的无参数的构造函数。但是在PHP中,子类的构造函数不会隐式地去调用父类的构造函数,需要开发者通过parent::__construct()来显式地去调用父类的构造函数。当子类没有定义构造函数的时候,它会继承父类的构造函数,但前提是父类的构造函数不能被定义为private。使用示例如下:<?php class BaseClass { function __construct() { print “Base constructor\n”; } } class SubClass extends BaseClass { function __construct() { parent::__construct(); print “Sub constructor\n”; } } // 会调用父类构造函数 $obj = new BaseClass(); //调用子类构造函数,子类构造函数会去调用父类构造函数 $obj = new SubClass();?>程序的运行结果为Base constructor Base constructorSub constructor从上面的讲解中可以发现,从PHP5开始多了一种构造函数定义的方法。为了实现不同版本PHP代码的兼容,在PHP5的类中找不到 __construct() 函数并且也没有从父类继承一个的话,那么它就会尝试寻找旧式的构造函数(与类同名的函数)。这种兼容的方法存在一个风险:在PHP5之前的版本中开发的类中已有一个名为 __construct() 的方法却被用于其他用途时,PHP5的类会认为这是一个构造函数,从而当类实例化时自动执行这个方法。从 PHP 5.3.3 开始,在命名空间中,与类名同名的方法不再作为构造函数。这一改变不影响不在命名空间中的类。2.析构函数析构函数是在PHP5引入的,它的作用与调用时机和构造函数刚好相反,它在对象被销毁时自动执行。析构函数__destruct()结构形式如下:function __destruct(){ /* 类的初始化代码*/} 需要注意的是,析构函数是由系统自动调用的,因此,它不需要参数。默认情况下,系统仅释放对象属性所占用的内存,并不销毁在对象内部申请的资源(例如,打开文件、创建数据库的连接等),而利用析构函数在使用一个对象之后执行代码来清除这些在对象内部申请的资源(关闭文件、断开与数据库的连接)。与构造函数类似,如果想在子类中调用父类的析构函数,那么需要显式地调用:parent::__destruct()。如果子类没有定义析构函数,那么它会继承父类的析构函数。当对象不再被引用时,将调用析构函数。如果要明确地销毁一个对象,那么可以给指向对象的变量不分配任何值,通常将变量赋值为NULL或者用unset()函数。示例代码如下:<?php class des{ function __destruct(){ echo “对象被销毁,执行析构函数<br>”; } } $p=new des(); /* 实例化类 / echo “程序开始<br>”; unset($p); / 销毁变量$p */ echo “程序结束”;?>四、面向对象的作用域范围有哪几种?在PHP5中,类的属性或者方法主要有public、protected和private三种类作用域,它们的区别如下:1)public(公有类型)表示全局,类内部、外部和子类都可以访问。默认的访问权限为public,也就是说,如果一个方法没有被public、protected或private修饰,那么它默认的作用域为public。2)protected(受保护类型)表示受保护的,只有本类或子类可以访问。在子类中,可以通过self::var或self::method访问,也可以通过parent::method来调用父类中的方法。在类的实例化对象中,不能通过$obj->var来访问protected类型的方法或属性。3)private(私有类型)表示私有的,只有本类内部可以使用。该类型的属性或方法只能在该类中使用,在该类的实例、子类、子类的实例中都不能调用私有类型的属性和方法。预告:PHP面试常考内容之面向对象(2)将于本周三(2019.2-13)更新。以上内容摘自《PHP程序员面试笔试宝典》书籍,该书已在天猫、京东、当当等电商平台销售。更多PHP相关的面试知识、考题可以关注公众号获取:琉忆编程库对本文有什么问题或建议都可以进行留言,将不断完善追求极致,感谢你们的支持。 ...

February 11, 2019 · 1 min · jiezi

❖ Python OOP 面向对象编程

参考:黑马程序员教程 - Python基础 面向对象OOP三大特性,且三个特性是有顺序的:封装继承多态封装指的就是把现实世界的事务,封装、抽象成编程里的对象,包括各种属性和方法。这个一般都很简单,不需要多讲。唯一要注意的就是:推荐从小往大开始封装、开发类。比如手枪,子弹这两个类,我们需要先定义和开发子弹的所有属性和方法,然后再去开发上一层的手枪。这样的话会很方便。反过来开发手枪的适合,发现写到一半写不下去要到子弹那里写,就很乱了。继承子类可以继承父类和父父类的所有属性、方法。继承格式:class Parent: def func1(self): passclass Son(Parent): def func2(self): func1()方法改写:子类在不满意时,也可以进行自己的改写父类的属性、方法。其中有两种情况:Overwrite 覆盖重写父类方法:只需要写一个同名函数即可覆盖。Extend 扩展父类函数:第一种方式(主要):写一个同名函数,并在其中通过super().func()引用父类方法。其中super是一个python builtin 特殊类,而super()即生成一个super的实例。在子类中生成super实例,会得到父类的引用。第二种方式(python 2.x以前使用):写一个同名函数,再通过ParentName.func(self)引用父类方法。但是不推荐,因为父类名称改变的话所有的子类都要改。私有不继承:子类能够继承的只是父类的公开内容,但是不包括父类的私有内容。如果要访问的话也可以,但是需要间接的调用父类再用方法调用私有内容。多继承Python中,子类是可以同时有多个父类的:也就是能够同时继承多个父类的所有属性、方法。继承格式:class Father: def func1(self): passclass Mother: def func2(self): passclass Son(Father, Mother): def func3(self): func1() func2()注意:如果多个父类间存在有同名的方法,那么会继承第一个父类的方法。MRO, Method Resolution Order查看继承顺序:通过类自带的.__mro__属性(MRO, Method Resolution Order),可以查看这个类的继承顺序。子类可以直接写FatherName.func()来调用父级函数。但是当子类用super().func()时候,python就会根据MRO顺序,由近到远逐次寻找,找到最近的上级则返回。用上例,如果是多继承的话,那么寻找顺序是:SON -> Father -> Mother -> object。查看类的内置属性和方法:dir(className)可以查看内置所有属性方法。Python内置的object基础类Python3开始使用新式的类定义,即默认让所有定义的类都自动继承一个叫object的内置基础类。object基础类定义了很多方便的属性。包括18项之多。而旧式的Python2.x时代,不继承object基础类,自己定义的类就只有__doc__和__module__两样内置属性而已。2.x时代,如果需要手动继承,如:class MyClass(object): pass多态多态是指,不同的子类对象调用相同的父类方法,会产生多态多样结果的编程特性。多态的前提是能够继承父类的方法,且能够重写改写父类的方法。多态的特点:是调用方法的技巧,而不影响类的内部设计可以增加代码灵活度def Father(): def work(self): do_job() def do_job(self): print(‘Farming on the field…’)def Son(Father): def do_job(self): print(‘Programming at an office…’)# —- Now let’s work —-Jason = Son()Jason.work()以上代码中,同样是work()函数,且要do_work()。但是,不同的人调用的是不同的do_work。Father调用自己的do_work,儿子因为自己重写了do_work,所以调用自己的方法。这就是多态——所继承的方法,不需要再特殊指定谁用什么方法,而对象会自动调用适合自己的方法。类与实例Python中,实例是一个对象,类也是一个对象,一切皆对象。但这也是Python OOP中引起很多麻烦的原因。实例对象非常好理解,也好用,直接用,就不说了。但是类对象就不那么好理解了。简单说,类对象也是一个标准的对象,有自己的属性和方法,只不过能够像模版一样生成多个实例对象而已。类对象有这两大研究点:类属性:就是能让所有实例访问和操作的公用厕所定义类属性:位于class的所有方法之外访问类属性:className.propertyName类方法:比较难理解,必须用到名为@classmethod的装饰器,函数的第一个参数必须是关键字cls,如同self。@classmethod装饰器:用来告诉解释器这是一个类方法,而不是实例方法。cls参数:类属性与实例属性这是Python OOP中困扰很多人的特点。但是其实不难理解,总结如下:class MyClass: # 在这个位置定义的,叫类属性。==等同于其它语言的“静态属性” # 这是每个实例共有的公用属性,相当于宿舍的公用洗澡间 count = 0 def init(self): # 用self.定义的,叫实例属性,是每个实例只自己所有的属性,selfish self.name = “Jason"访问类属性的方法有两种:ClassName.propertyName:推荐,直接用类名访问类属性。Instance.propertyName:不推荐用实例名访问类属性,因为如果需要写入操作,那么这种方法只会给自己添加一个实例属性,而不会影响类属性。动态添加类属性方法一:>>> MyClass.newAttribute = ‘I am a class attribute’>>> print( MyClass.newAttribute )‘I am a class attribute’方法二:装饰器# Customized decorator for classpropertyclass classproperty(object): def init(self, getter): self.getter= getter def get(self, instance, owner): return self.getter(owner)class MyClass: @classproperty def newAttribute(cls): return ‘I am a class attribute.’>>> print( MyClass.newAttribute )‘I am a class attribute’之所以把方法封装为一个类属性,是因为我们有时候需要根据其它类属性来定制这个类属性。而一般情况下,我们没法在类属性定义的时候获得当前的类或类中其它的属性。类方法类方法如同类属性,是属于全类的方法,但是(推荐)只用来访问类属性。类方法:比较难理解,必须用到名为@classmethod的装饰器,函数的第一个参数必须是关键字cls,如同self。@classmethod装饰器:用来告诉解释器这是一个类方法,而不是实例方法。cls参数:如同self,用来指代当前的类。注意:@classmethod和cls都是关键字,不能改。代码:class MyClass: # 定义一个“类属性” count = 0 # 这里开始定义“类方法” @classmethod def func(cls): print(cls.count)类静态方法类中的staticmethod装饰器同样是python基础类object的一个用于包装、装饰的方法。一旦在类方法前放上装饰器@staticmethod,方法就会转换为一个静态方法。静态方法就是一个非常独立的方法:既不访问实例的信息,也不访问类的信息。代码:class MyClass: # 定义一个“类属性” count = 0 # 这里开始定义“类方法” @staticmethod def func(): passProperty属性类中的property装饰器,也是python基础类object的一个用于包装、装饰的方法。一旦类方法前放上装饰器@property,方法就会转换为一个类属性。很多时候把方法伪装成属性,是非常方便的。class MyClass: # 这里开始定义由方法转换为“类属性” @property def name(self): return “Jason"c = MyClass()print( c.name )在继承object基础类的情况下,python给出了三种类属性装饰,对应三种操作:读取:@property写入:@name.setter删除:@name.deleter也就是说,当你读取类属性my_name的时候,会调用被@property修饰的方法;当你修改my_name当时候,会调用被@my_name.setter修饰的方法;当你删除这个属性时,会调用被@my_name.deleter修饰的方法。注意:其中@property, @.setter, @.deleter,这是固定的名字,不能改。三种操作所修饰的三个函数,必须都是同一个名字:即“类属性”名。代码:class MyClass: # 这里开始定义由方法转换为“类属性” @property def name(self): return “Jason” @name.setter def name(self, value): self.name = value @name.deleter def name(self): del “Jason"c = MyClass()print( c.name ) # READc.name = “Brown” # SETdel c.name # DELETEproperty属性的应用很多OOP语言,针对property属性,一般操作是:一个私有属性,配合两个公有方法。如:class MyClass: def init(self): self.__name = “Jason” def get_name(self): return self.__name def set_name(self, value): self.__name = valuec = MyClass()# 开始调用c.set_name(“Brownee”)print( c.get_name() )在Python下,可以利用装饰器改为以下代码,极大方便调用的过程:class MyClass: def init(self): self.__name = “Jason” @property def name(self): return self.__name @name.setter def name(self, value): self.__name = valuec = MyClass()# 开始调用c.name = “Brownee"print( c.name ) ...

January 10, 2019 · 2 min · jiezi

编程--基本概念

1.面向过程(PROCEDURE ORIENTED)1).具体化,流程化2).性能高3).算法+数据结构2.面向对象(OBJECT ORIENTED)(OO)1).模型化2).易维护,易复用,易扩展3.面向对象编程(OOP)1).继承 允许在现存的组件基础上创建子类组件,这统一并增强了多态性和封装性 A).重载(以统一的方法处理不同数据类型) 一个类的多态性表现 B).重写(方法重写) 父子类多态性体现2).封装(信息封装) 确保组件不会以不可预期的方式改变其它组件的内部状态3).多态 组件的引用和类集会涉及到其它不同类型的组件,而且引用组件所产生的结果得依据实际调用的类型4.面向切面编程(ASPECT ORIENTED PAROGRAMMING)(AOP)1).切面 项目模块中某些业务逻辑(业务需要一定共性)2).解耦,提高程序可重用性,提高开发效率5.三层架构、MVC、MVP、MVVM1).三层架构–界面层(User Interface Layer-Business Logic Layer-Data access Layer 界面–业务逻辑–数据访问) A).界面层(UIL) 与用户交互 B).业务逻辑层(BLL) 实现业务逻辑。业务逻辑具体包含:验证、计算、业务规则等 C).数据访问层(DAL) 与数据库打交道。主要实现对数据的增、删、改、查 2).MVC(Model-View-Controller 模型–视图–控制器) A).Model(模型) 业务逻辑、业务模型、业务操作、数据模型。定义了数据修改和操作的业务规则 B).View (视图) UI组件。接收Controller数据,降Model转化成UI C).Controller(控制器) 处理流入请求 D).特点 View和Model分离(1978 Trygve Reenskaug) E).流程 View⇒Controller⇒Model⇔View 3).MVP(Model-View-Presenter MVC改良模式(View与Model完全解耦)) A).Model(模型) 业务逻辑、业务模型、业务操作、数据模型。定义了数据修改和操作的业务规则 B).View (视图) UI组件。接收Controller数据,降Model转化成UI C).Presenter(控制器) 处理View背后所有UI事件(一个Presenter只映射一个view) D).特点 View和Presenter双向交互(IBM的子公司Taligent提出) E).流程 View⇔Presenter⇔Model 4).MVVM(Model-View-View Model MVP中把P层削弱为VM层,部分简单的逻辑职责分给了View层) A).Model(模型) 业务逻辑、业务模型、业务操作、数据模型。定义了数据修改和操作的业务规则 B).View (视图) UI组件。接收Controller数据,降Model转化成UI C).View Model(控制器) 负责暴漏方法,命令,其他属性来操作View的状态,触发View自己的事件 D).特点 View和View Model双向数据绑定关系 E).流程 View⇒View Model⇔Model ...

December 26, 2018 · 1 min · jiezi

谈一谈javascript面向对象

从今天起我们开始讨论javascript的面向对象面向对象概念理解面向对象语言有个标志=>它们都具有类的概念,通过类可以创建任意多个具有相同属性和方法的对象。面向对象有三大特性封装继承多态但JS中对象与纯面向对象语言中的对象是不同的JS中的对象:无序属性的集合,其属性可以包含基本值、对象或者函数。可以简单理解为JS的对象是一组无序的值,其中的属性或方法都有一个名字,根据这个名字可以访问相映射的值(值可以是基本值/对象/方法)。创建对象的基本方法我们前面在讲原型链的时候说过,两种创建对象的方法对象字面量(对象直接量)这是最快的一个????: const hero = { name:“欧阳锋”, nickname:“西毒”, doSth:function(){ console.log(‘灵蛇杖法’); }⚠️创建对象的属性名并不强制使用引号包裹,除了以下几种情况属性名中包含空格属性名中包含连字符(中划线)属性名中包含保留字const obj={ “go home”:“包含了空格”, “go-home”:“包含了连字符”, “for”:“这是保留字”}new 实例化一个对象通过new运算符创建并实例化一个新对象,new后面是一个构造函数const hero = new Object()hero.name = “欧阳锋"hero.nickname = “西毒"hero.doSth = function () { console.log(‘灵蛇杖法’);}两种创建方法是一样的创建对象通过以上两种方式似乎足够了,但是当场景稍微复杂一点,问题就显现出来了当我门创建很多结构相同的对象时,会产生大量的重复代码,为了解决这个问题,出现了一个解决方案工厂模式工厂模式抽象了创建具体对象的过程,因为javascript无法创建类,开发人员就发明了一种函数,用函数来封装以特定接口创建对象一个????:function createHero(name,nickname,doSth) { const obj = new Object() obj.name=name obj.nickname=nickname obj.doSth = function () { console.log(doSth); } return obj}const hero1 = createHero(“欧阳锋”,“西毒”,“灵蛇杖法”)const hero2 = createHero(“黄药师”,“东邪”,“碧海潮生曲”)console.log(hero1)看下输出:hero1和hero2都直接继承自Object实例,工厂模式就是像工厂一样来创建对象,创建的每一个对象都是通过new Object()来创建的后来,开发人员有发现了更好的模式构造函数模式我们之前讨论过,通过使用自定义构造函数来实例化对象function Hero(name, nickname, doSth) { this.name = name this.nickname = nickname this.doSth = function () { console.log(doSth); }}const hero3 = new Hero(“欧阳锋”,“西毒”,“灵蛇杖法”)const hero4 = new Hero(“黄药师”,“东邪”,“碧海潮生曲”)console.log(hero3);注意⚠️:创建自定义构造函数,函数名首字母大写,用来和非构造函数进行区分我们继续看下输出:hero3是通过Hero实例化出来的,所以hero3先继承自Hero要创建Hero的新实例,必须使用new操作符,以这种方式调用构造函数实际上会经历以下四个步骤,创建一个新对象将构造函数的作用域赋给新对象(因此this就指向了这个新对象)执行构造函数中的代码(为这个新对象添加属性)返回新对象hero3和hero4都是Hero的实例,同时也是Object的实例instanceof用于判断一个变量是否某个对象的实例console.log(hero3 instanceof Hero);//=>trueconsole.log(hero3 instanceof Object);//=>trueconsole.log(hero4 instanceof Hero);//=>trueconsole.log(hero4 instanceof Object);//=>true属性和方法(公有&私有)????的????中,我们将属性和方法绑定在了构造函数Hero中的this上,hero3和hero4都可以访问这些属性绑定在this上的属性我们称之为公有属性绑定在this上的方法我们称之为公有方法也就是说通过构造函数Hero实例化出来的对象是可以方位公有属性和公有方法的既然有公有属性和公有方法,就一定会有私有属性和私有方法我们做一下调整function Hero(name, nickname, doSth) { let test = “私有属性” function method(){console.log(“私有方法”);} this.name = name this.nickname = nickname this.doSth = function () { console.log(doSth); }}const hero3 = new Hero(“欧阳锋”,“西毒”,“灵蛇杖法”)const hero4 = new Hero(“黄药师”,“东邪”,“碧海潮生曲”)console.log(hero3);看下输出:在hero3中是不存在构造函数的私有属性和私有方法的如果我们这创建完构造函数后,追加一下属性和方法,会怎么样呢?试试看 function Hero(name, nickname, doSth) { let test = “私有属性” function method(){console.log(“私有方法”);} this.name = name this.nickname = nickname this.doSth = function () { console.log(doSth); } } Hero.localAttr=“测试属性” Hero.localMethod=function(){ console.log(‘测试方法’); } Hero.prototype.proAttr=“原型属性” Hero.prototype.proMethod=function(){ console.log(‘原型方法’); } const hero3 = new Hero(“欧阳锋”,“西毒”,“灵蛇杖法”) const hero4 = new Hero(“黄药师”,“东邪”,“碧海潮生曲”) console.log(hero3); console.log(’localAttr测试属性:’,hero3.localAttr); console.log(“localMethod测试方法:",hero3.localMethod); console.log(“proAttr原型属性:",hero3.proAttr); console.log(“proMethod原型方法:",hero3.proMethod);看输出:创建完实例对象后,通过.运算符添加的属性是类静态公有属性(实例化的对象无法访问)通过.运算符添加的方法是类静态公有方法(实例化的对象无法访问)通过原型链添加的属性是公有属性(实例化的对象可以访问)通过原型链添加的方法是公有方法(实例化的对象可以访问)今天就到这里,明天不见不散收集整理了一套js进阶教程,公众号后台回复“js进阶”即可领取参考文献:《javascript高级程序设计》(第三版)《javascript设计模式》《javascript语言精粹》(修订版)原文链接 ...

December 14, 2018 · 1 min · jiezi

php微框架 flight源码阅读——框架初始化、Loader、Dispatcher

在自动加载实现完成后,接着new flightEngine()实例化了下框架的核心类Engine,这个类翻译过来名字就是引擎发动机的意思,是flight的引擎发动机,很有想象力吧。public static function app() { static $initialized = false; if (!$initialized) { require_once DIR.’/autoload.php’; self::$engine = new \flight\Engine(); $initialized = true; } return self::$engine;}在实例化Engine这个类的时候,当前类的构造方法进行了对框架的初始化工作。public function __construct() { $this->vars = array(); $this->loader = new Loader(); $this->dispatcher = new Dispatcher(); $this->init();}接着来看init方法都做了什么,将初始化状态标记为静态变量static $initialized,判断如果为true,将$this->vars以及$this->loader、$this->dispatcher中保存的属性重置为默认状态。static $initialized = false;$self = $this;if ($initialized) { $this->vars = array(); $this->loader->reset(); $this->dispatcher->reset();} 接下来将框架的Request、Response、Router、View类的命定空间地址register(设置)到Loader类的classes属性中。// Register default components$this->loader->register(‘request’, ‘\flight\net\Request’);$this->loader->register(‘response’, ‘\flight\net\Response’);$this->loader->register(‘router’, ‘\flight\net\Router’);$this->loader->register(‘view’, ‘\flight\template\View’, array(), function($view) use ($self) { $view->path = $self->get(‘flight.views.path’); $view->extension = $self->get(‘flight.views.extension’);}); flight/core/Loader.phppublic function register($name, $class, array $params = array(), $callback = null) { unset($this->instances[$name]); $this->classes[$name] = array($class, $params, $callback);}再接下来就是将框架给用户提供的调用方法,设置到调度器Dispatcher类的events属性中。// Register framework methods$methods = array( ‘start’,‘stop’,‘route’,‘halt’,’error’,’notFound’, ‘render’,‘redirect’,’etag’,’lastModified’,‘json’,‘jsonp’);foreach ($methods as $name) { $this->dispatcher->set($name, array($this, ‘_’.$name));}flight/core/Dispatcher.php/** * Assigns a callback to an event. * * @param string $name Event name * @param callback $callback Callback function /public function set($name, $callback) { $this->events[$name] = $callback;}接下来呢,就是设置框架的一些配置,将这些配置保存在Engine类的vars属性中。// Default configuration settings$this->set(‘flight.base_url’, null);$this->set(‘flight.case_sensitive’, false);$this->set(‘flight.handle_errors’, true);$this->set(‘flight.log_errors’, false);$this->set(‘flight.views.path’, ‘./views’);$this->set(‘flight.views.extension’, ‘.php’);flight/Engine.php/* * Sets a variable. * * @param mixed $key Key * @param string $value Value /public function set($key, $value = null) { if (is_array($key) || is_object($key)) { foreach ($key as $k => $v) { $this->vars[$k] = $v; } } else { $this->vars[$key] = $value; }}最后一步的操作,当调用框架的start方法时,给其设置一些前置操作,通过set_error_handler()和set_exception_handler()设置用户自定义的错误和异常处理函数,如何使用自定义的错误和异常函数,可以看这两个范例:https://segmentfault.com/n/13…https://segmentfault.com/n/13…。// Startup configuration$this->before(‘start’, function() use ($self) { // Enable error handling if ($self->get(‘flight.handle_errors’)) { set_error_handler(array($self, ‘handleError’)); set_exception_handler(array($self, ‘handleException’)); } // Set case-sensitivity $self->router()->case_sensitive = $self->get(‘flight.case_sensitive’);});$initialized = true;/* * Custom error handler. Converts errors into exceptions. * * @param int $errno Error number * @param int $errstr Error string * @param int $errfile Error file name * @param int $errline Error file line number * @throws \ErrorException /public function handleError($errno, $errstr, $errfile, $errline) { if ($errno & error_reporting()) { throw new \ErrorException($errstr, $errno, 0, $errfile, $errline); }}/* * Custom exception handler. Logs exceptions. * * @param \Exception $e Thrown exception /public function handleException($e) { if ($this->get(‘flight.log_errors’)) { error_log($e->getMessage()); } $this->error($e);}在$this->before()操作中,会将前置操作设置到Dispatcher类的filters属性中。这些操作完成后,将$initialized = true。/* * Adds a pre-filter to a method. * * @param string $name Method name * @param callback $callback Callback function /public function before($name, $callback) { $this->dispatcher->hook($name, ‘before’, $callback);}flight/core/Dispatcher.php/* * Hooks a callback to an event. * * @param string $name Event name * @param string $type Filter type * @param callback $callback Callback function */public function hook($name, $type, $callback) { $this->filters[$name][$type][] = $callback;} ...

December 6, 2018 · 2 min · jiezi