不要重复自己
注意:重复的代码不一定违反DRY原则,不重复的代码也有可能违反DRY原则
代码逻辑重复
功能语义重复
代码重复执行
比如UserService有两个方法,一个IsValidUserName校验用户名,一个IsValidPassword校验密码
type UserService2 struct{}
func (s *UserService2) IsValidUserName(username string) bool {
if username == "" {
return false
}
//只包含a~z、0~9、.
for i := range username {
u := username[i]
if ((u >= 'a' && u <= 'z') || (u >= '0' && u <= '9') || u == '.') == false {
return false
}
}
return true
}
func (s *UserService2) IsValidPassword(password string) bool {
if password == "" {
return false
}
//只包含a~z、0~9、.
for i := range password {
u := password[i]
if ((u >= 'a' && u <= 'z') || (u >= '0' && u <= '9') || u == '.') == false {
return false
}
}
return true
}
这里IsValidUserName和IsValidPassword两个方法的代码逻辑是一样的,但是它们的功能职责是不一样的,所以我们不认为违反DRY原则,不需要合并这两个方法为一个方法
因为这两个方法的代码逻辑相同可能是暂时的,后续比如用户名校验的规则变了,那么用户名校验的代码就需要修改,如果合成一个方法那么又需要将方法拆开
比如IpUtil有两个校验Ip是否有效的方法,一个IsValidIp方法,一个CheckIfIpValid方法
IsValidIp方法采用正则表达式实现校验,CheckIfIpValid方法使用系统自带的类库实现校验,这两个方法虽然代码逻辑不一样,但是功能语义一样,我们认为违反DRY原则
我们应该统一Ip校验逻辑,统一为一个校验方法,这样在需要修改Ip校验规则时只需要修改那唯一一个方法即可,不会出现遗漏修改方法的情况,避免出现问题
比如UserService有一个login方法,用来判断用户是否存在,如果存在那么返回用户信息
type UserInfo struct {
id string
name string
}
type UserRepo struct{}
func (r *UserRepo) CheckUserExist(username string) bool {
if username == "" {
return false
}
//调用DB查询username是否存在
return true
}
func (r *UserRepo) GetUserByUserNameAndPassword(username string, password string) *UserInfo {
if username == "" {
return nil
}
if password == "" {
return nil
}
//调用DB根据username、password查询用户信息
return &UserInfo{
id: "1",
name: "Test",
}
}
type UserService3 struct {
userRepo *UserRepo
}
func (s *UserService3) Login(username string, password string) *UserInfo {
if s.userRepo.CheckUserExist(username) {
return nil
}
return s.userRepo.GetUserByUserNameAndPassword(username, password)
}
上述代码存在两处重复执行的地方,违反DRY原则
代码复用:是一种行为,写新需求的时候尽量复用原来已存在的代码
代码可复用性:是指代码可被复用的能力,我们写代码的时候,要尽量让代码可复用
DRY原则:是一个设计原则,不要写"重复"的代码,这里的重复有多层含义
尽管这三者在概念上有区别,但是它们的目的都是减少代码量,提高可读性、可维护性。除此之外,复用线上已经验证过没问题的老代码,出现bug的可能性也比自己写要低
灵活应用DRY原则
可以采用"Rule Of Three"原则,第一次写代码的时候,如果当下没有复用的需求,未来复用的需求也不明确,那么就不需要考虑代码的可复用性;第二次写代码的时候,发现之前某段代码可以复用,那么就可以将这段代码抽成单独的模块、类或者函数进行复用