概念最重要
昨天继续补充学习了关于 Ruby 的一些基础知识,继续整理一下。
继承和多重继承
之所以会出现「继承」这样的功能,主要目的是遵循 DRY (Don’t Repeat Yourself)的原则。
比如说已经定义一个 类 A,具有的方法有 a1,a2 和 a3。现在要新定义一个 类 B,类 B 的属性跟 类 A 一样,也同样具有方法 a1 ~ a3,不同的是类 B 还需要一个方法 a4。这个时候,按照 DRY 的原则,我们只应该需要追加定义方法 a4 就好了,其他已经定义过的不需要重新定义。这个时候就可以用到「继承」的概念。类 B 继承于 类A,这样 类 B 就具有了 类 A 的全部属性和全部方法,然后可以再次追加或者重写某些方法。
假如现在的情况是已经定义了 类 A 和类 B,要新定义一个类 C,这个类 C 需要同时具有类 A 和类 B 的方法。直观的想法是,我们要使得类 C 同时继承于类 A 和类 B,这就是所谓的「多重继承」。多重继承在某些程度上带来了便利,但是因为每一个类都可能有多个父类,使得继承链变得非常复杂。
Ruby 是不支持多重继承的。
module 模块的解决方法
那 Ruby 是如何解决类似于多重继承的问题的呢?其实 Ruby 用了 Maxin 的方法实现了类似多重继承的功能。这里要引入一个概念,就是 Module,即模块。
模块其实是一种特殊的类,是不能实例化的类。 上述的例子,我们将类 B 写成模块 B,然后使类 C 继承于类 A,再将模块 B 注入到类 C 中,这样类 C 就同时具有了 A 和 B 的方法,这种将模块注入到类中的用法就叫做 maxin。
通过 maxin,避免了多重继承,使得继承链依然清晰明了,而模块引用也实现了类似多重继承的功能。
module 作为命名空间的使用
Module 除了解决多重继承的问题,还有一个重要的作用,就是 namespace,即命名空间。
在 Rails 中的应用非常常见,比如说一个 User 的模块,分为用户和管理员的角色,两者的所拥有的权限是不同的,也就是方法是不同的,换言之,我们需要有两个 controller 对其分别管理。
在目录 app/controllers
下我们有一个 UsersController
,是对应普通用户。在目录 app/controllers/admin
下还有一个 UsersController
,对应的是管理员。观察管理员的 controller
class Admin::UsersController < ApplicationController
# some stuff..
end
Admin::UsersController
的意思是,当下 UserController
这个类是在 Admin
这个模块下的,跟前台的 UsersController
虽然同名,却是不同的两个类。这时候,模块 module 就起到了命名空间的作用,可以将一些类分开来管理。
include 和 extend
在 Ruby 中,include
和 extend
都是 maxin 的方法。不同的是 include
注入的是实例方法,extend
注入的是类方法。
include 和 prepend
include
和 prepend
都是注入实例方法,但两者得到的继承链不同。比如说
# example1
class User
include admin
# ...
end
# example2
class User
prepend admin
# ...
end
第一个例子,在继承链中,include
进来的 Admin 是变成 User 这个类的父类,也就是说继承链是这样的: [User, Admin, …]。
对于第二个例子,用了 prepend
,在继承链上,模块 Admin 会在 User 之前,也就是这样: [Admin, User, …]。
值得注意的是,在对一个实例调用某个方法时,Ruby 查找这个方法的顺序是按照继承链的顺序查找的。
private 和 protected
在一个类里面定义方法,默认的属性是 public
,也就是说定义的这个方法是可以被外部访问和调用的。比如说在 Rails 项目中,对于管理员角色的判定,一般可以在 User 这个 Model 下定义个方法
class User < ApplicationRedcord
def is_admin?
is_admin
end
end
这个方法是可以在 Controller 里被调用的,也就是说 is_admin?
这个方法属性是 public
。
如果想让定义的方法局限在本类里面,不被外部访问到,就可以用 private
或者 protected
。
两者的区别在于,对于 protected
的方法,外部无法访问,但是该类的子类是可以访问到的。而对于 private
的方法,是最严格的,外部完全无法访问,包括该类的子类也同样无法访问。
目前,private
最常见的使用场景是在 controller 里定义 强参数
的时候
class UserController < ApplicationAction
# some stuff...
private
def user_params
params.require(:user).permit(:username)
end
end
还有一些现在还很模糊的内容,需要在不断的实践来继续理解。
学习任何一项领域的技能,就是理解这个领域的所有关键概念,然后捋清各个概念之间的联系。如何理解新的概念和建立概念之间的联系呢?实践实践再实践,总结总结再总结。
今天继续学习 Rails 部分的一些基础概念。