本文作者:
前言
设计模式的学习过程中往往有四个境界
- 没学过以前是一点也不懂,在特定的场景下想不到一种通用的设计方式,设计的代码比较糟糕。
- 学了几个模式以后很开心想着到处用自己学过的模式,于是会造成误用模式而不自知。
- 学了很多设计模式,感觉诸多模式极其相似,无法分清模式之间的差异,但深知误用有害,应用时有所犹豫。
- 灵活应用模式,甚至不应用具体的某种模式也能设计出非常优秀的代码,以达到无剑胜有剑的境界。
本系列将会和大家一起从了解面向对象开始,再深入到常用的设计模式,一起探索TypeScript配合设计模式在我们平时开发过程中的无限可能,设计出易维护、易扩展、易复用、灵活性好的程序。
什么是面向对象
面向对象可以说是一种编程风格,在代码的编写过程中更注重围绕对象而不是单个函数。 世界上有很多人和事物,每一个都可以看做一个对象,而每个对象都有自己的属性和行为,对象与对象之间通过方法来交互。面向对象是一种以“对象”为中心的编程思想,把要解决的问题分解成各个对象,建立对象的目的不是为了完成一个步骤,而是为了描叙某个对象在整个解决问题的步骤中的属性和行为。
TypeScript面向对象基础
下面我们会用一些小例子和大家一起学习面向对象编程的一些概念。
第一步我们先简单搭建一个环境,让我们可以更好的调试我们的代码。
先新建一个文件夹作为我们练习的小仓库
全局安装 tsc 在该目录下运行 npm install tsc -g
在该文件夹下生成一个tsconfig.json文件,运行tsc --init
我们再新建两个文件夹分别为dist和src文件夹,用来存放ts源文件和编译后的js文件
修改一下tsconfig.json文件,添加如下两个配置
其中rootDir是指我们写的ts文件路径,outDir是指我们编译后输出的文件路径。
新建一个index.html文件:
为了更加方便的看到我们代码的输出和调试,我们安装一个vscode插件"live server"它可以作为一个实时服务器实时查看开发的网页。
安装好之后我们就可以直接右键index.html文件在浏览器打开了
至此我们的环境就搭建完成了。
类与实例
我们先看一下类与实例的定义
类:具有相同的属性和功能的对象的抽象的集合。
实例:实例也可以称为对象,是一个自包含的实体,用一组可识别的特征和行为来标识
假设我们现在有一个圆,它有一个方法是画圆,我们用面向对象的方式去实现它将会是如下这样的
// 有一个圆的类
class Circle {
draw() {
console.log('draw')
}
}
// 实例化圆类
const circle = new Circle()
// 调用画圆的方法
circle.draw()
我们可以尝试运行以上代码,我们先在之前搭建的项目的src文件夹下新建index.ts文件,复制以上代码进去,
然后我们在终端运行 tsc -watch
这个时候我们可以发现dist文件夹下多出了一个index.js文件,然后我们在index.html文件中添加script标签引入**"./dist/index.js"**文件
然后我们用刚才的方法在浏览器中打开,我们就可以看到控制台输出了
构造函数
构造方法其实就是对类进行初始化,它会在每次new一个类的时候自动调用,在es6的类中有一个**"contructor"**的特殊方法,代表构造函数
假设现在我们的圆每次初始化的时候需要有一个半径,我们需要对上面的代码做一些改造
class Circle {
// 声明一个Circle的私有变量radius代表半径
private radius;
// 定义Circle类的构造函数,需要传入一个number类型的参数
constructor(radius: number) {
// 将传入的参数复制给私有变量radius
this.radius = radius
}
draw() {
console.log('draw,radius: ', this.radius)
}
}
// 实例化对象的时候必须传入构造函数所需要的参数
const circle = new Circle(1)
circle.draw()
经过了如上的改造之后,我们每次创建一个圆的实例时都需要传入一个半径值,那这个圆每次创建的时候就会自动带有一个半径的属性了。我们保存代码运行效果如下:
方法重载
方法重载定义:如果有两个方法的方法名相同,但参数不一致,那么就可以说一个方法是另一个方法的重载。
但是在我们js里面,存在两个同名方法的话是会被覆盖的,所以在我们js语言里方法重载其实不需要重复声明函数,只需要根据函数传入参数的不同做不同的处理逻辑就可以实现了。
还是拿我们上面的例子做演示。
现在我们每次创建一个圆实例都必须要传入一个半径,那我们可不可以在传入半径的时候使用我们传入的值做半径,不传入的时候用一个默认的半径值呢?
class Circle {
private radius;
// 定义Circle类的构造函数,把radius参数设置为可选参数
constructor(radius?: number) {
if (radius) {
this.radius = radius
} else {
this.radius = 1
}
}
draw() {
console.log('draw,radius: ', this.radius)
}
}
// 实例化circle1不传入参数,使用默认值
const circle1 = new Circle()
// 实例化circle2传入参数2
const circle2 = new Circle(2)
circle1.draw()
circle2.draw()
我们只需要把radius参数设置为可选,然后再根据传入参数的不同,做不同的事情就实现了这个功能。
运行以上代码效果:
属性与修饰符
属性
假设我们现在圆现在还需要有所在位置的信息并且可以通过实例去访问这个信息,那我们可以怎么做呢
class Circle {
private radius;
// 声明一个保存位置的公共属性
public location: object;
constructor(radius?: number) {
if (radius) {
this.radius = radius
} else {
this.radius = 1
}
// 初始化位置信息
this.location = {x: 1, y: 1}
}
draw() {
console.log('draw,radius: ', this.radius)
}
}
const circle1 = new Circle()
circle1.draw()
// 直接访问位置信息
console.log(circle1.location)
运行以上代码:
这个位置的信息就是圆的属性,我们可以直接通过实例访问到圆的内部属性。
但是通过这种方式我们可以在外部随意修改圆的location属性比如
class Circle {
private radius;
public location: object;
constructor(radius?: number) {
if (radius) {
this.radius = radius
} else {
this.radius = 1
}
this.location = {x: 1, y: 1}
}
draw() {
console.log('draw,radius: ', this.radius)
}
}
const circle1 = new Circle()
circle1.draw()
// 修改location属性
circle1.location = 123
console.log(circle1.location)
运行以上代码我们会发现我们location的值被改动了,而且数据结构与原来的也不符合
这个时候我们可以用es6的getter、setter访问器来解决这个问题。
class Circle {
private radius;
// 重新声明为私有变量
private location: object;
constructor(radius?: number) {
if (radius) {
this.radius = radius
} else {
this.radius = 1
}
this.location = {x: 1, y: 1}
}
// 通过get访问器提供对外访问内部属性的功能
get Location() {
return this.location
}
// 对属性的赋值做一些值的验证,如果不符合规则就抛出异常
set Location(obj: object) {
if (!obj.x || !obj.y) {
throw new Error('Invalid location')
}
this.location = obj
}
draw() {
console.log('draw,radius: ', this.radius)
}
}
const circle1 = new Circle()
circle1.draw()
// 修改location属性
circle1.Location = 123
console.log(circle1.Location)
运行以上代码
我们看到控制台抛出了我们写的异常,说明此次属性赋值不符合规范,这样我们就做到了对属性值的保护。
修饰符
在上面的各种例子中,我们看到属性声明前面都会有一个值 private或者public,这其实就是修饰符。
我们可以在方法或者属性声明的前面添加修饰符,在TypeScript中如果属性或者方法声明不添加修饰符,则默认为public
修饰符通常有以下几类
public
表示它所修饰的类成员可以允许其他任何类来访问,公有的
private
表示只允许同一个类的成员访问,其他类包括子类也无法访问,私有的
protected
表示它所修饰的类成员可以在派生类(子类)中访问
readonly
关键字将属性设置为只读,只读属性必须在声明时或构造函数里被初始化
上面的例子中location属性就是一个例子,一开始我们给它设置为public让类外部也可以随意调用,之后我们不想让外部直接调用到这个属性,所以我们把它设置为了private,改用getter setter的方式去对外提供对location属性的读取和修改。
总结
通过今天这篇文章我们简单介绍了面向对象的一些基本概念,后面还有面向对象的最重要的三大特征六大原则和常用的设计模式等内容将在接下来的文章中慢慢给大家揭晓,敬请期待。。。
参考文章
- 《大话设计模式》
- zhuanlan.zhihu.com/p/28427324
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!