当前位置: 首页 > 网站技术 > 正文

我的天,你知道吗?我发现我犯了一个大错,而且是大错特错。

我:我不应给误解你,OOP。我为之前做的事情道歉,我不该这么做。

OOP:你做了什么?

我:我浅显的把你理解成了一种编程语言的分类,只看到你的外表(继承,封装,多态)

OOP:哦,你只看到了我的外表。我不是哪类语言,请你了解我的灵魂。

我:我在这里就是想要了解你的灵魂和思想,请你原谅我。

OOP:OK,我原谅你,但是你要保证,以后遇到别人不要再这样的介绍我,好吗?

我:当然,为了能更好的的介绍你,我可以问你几个问题吗?

OOP:好,你问吧。

我:OOP 你不是编程语言的分类,那么你是什么呢?或者定义是什么?

OOP:我是一种设计思想,一种架构风格,一种解决复杂问题的模式,一种style, you know?

OOP: 我的创造者是这么解释的:OOP应该体现一种网状结构,这个结构上的每个节点“Object”只能通过“消息”和其他节点通讯。每个节点会有内部隐藏的状态,状态不可以被直接修改,而应该通过消息传递的方式来间接的修改。你能理解吗?

我:稍微有点难,你能举个例子来谈谈吗?教科书里面都会有动物-猫-狗,告诉我们什么是继承。

OOP:OMG,现在教科书是这么教的吗?你应该把他烧掉,这是什么例子,简直狗屎。他根本不懂什么是OOP, 简直是在侮辱我。

我:不要生气。现在告诉大家你真正的样子,表达你的思想,让我们重新认识你,好吗?

OOP:脾气有点暴,不好意思啊。

我:没关系,现在你可以好好介绍让大家真正的了解你。

OOP:好,首先说明我不是某一类编程语言的统称,我是一种设计思想,一种解决复杂问题的模式。大家对OOP的印象可能还是“万物皆对象,一切皆对象”,OOP特性继承,封装,多态的基本面上,由于最开始的理念错误,在编码时心想只要使用面向对象的编程语言就不会有问题,就会把代码的写的很好,然而一切都向着反方向走。一切的根源就是对OOP的错误认知造成的,继承就是复用,封装就是内聚,多态就是解耦,就是这样你的代码才会变成一坨。高复用,高内聚,高解耦只是OOP编程的结果,继承,封装,多态是OOP思想编码理念,不是设计模式。好多人吧他们当成设计模式,这样肯定不行。

我:我一直使用继承,封装,多态当做设计模式编写代码,但是写到最后越来越差,原来是这样的啊。那应该如何编码呢?

OOP:继承,封装,多态,他们之间是相辅相成的关系,是编码当中贯穿的理念。OOP规定“obj”是封闭的,“obj”和“obj”通讯只能通过“消息”传递,”obj”的状态只能通过消息间接的被修改。先从OOP的特性了解开始,传统方式是我使用继承完成代码的复用,使用封装完成代码的内聚,使用多态完成代码的解耦,就是我用这个东西去解决问题。看似没有问题,当我们细想就不行了。我们的目标都是解决问题,流程是: 找到问题 -> 解决方案 -> 选择工具 -> 解决问题,这样的流程没有错吧。那么之前的方案是什么样的呢?旧的流程是: 找到问题 -> 选择工具-> 解决方案 -> 解决问题。可以看出来在 “选择工具” 和 “解决方案”是完全倒置了,就像是我们拿着筷子进了西餐厅。我们想着使用继承,封装,多态,却是在错误的使用他们。

我:听君一席话,茅塞顿开。原来自己一直在错误的使用OOP,有时候还会想OOP怎么这么坑,一堆问题。那么我应该如何改正呢?如何才能把自己的代码写成OOP呢?

OOP:小伙子,悟性很高哦。你说了一句很正确的话,“如何才能把自己的代码写成OOP”而不是“我应该使用什么OOP语言才能写好代码”。首先思想转变很好,没有不好的语言只有写不好的代码。

OOP:先谈继承,现在语言都是单继承,除了C++是多继承。多继承变成单继承的原因,在我说完之后大家可以猜猜原因。继续谈继承,继承就是子类拥有父类所有的属性和方法。大家之前的做法可能就是代码复用和抽象多态。继承能够做到的事情很多,可以很方便的代码复用,轻易的完成多态,但是继承的真正意思是“继承人”的意思。就是说使用继承并不是在确定父子关系,而是拿走需要继承的函数和属性(相当是继承人拿走所继承的财产),也就是“拿走”的意思。子类继承父类,意思是把父类的那些函数和属性拿来直接使用,减少代码的重复开发,达到复用。不是大家普遍的理解继承就是找个“爹”,不仅拿走“爹”的所有财富,而且竟然还把“爹”的遗嘱内容“规则”随意改写,What are you doing? 就是这种错误的认知,现在的OOP语言才会把多继承变成单继承,防止大家写出更加混乱的代码,可是这样反而让大家更加坚定了抽象父类的使用(单继承简单了),和随意的继承复写父类方法,代码依然混乱。为了让大家减少混乱,大神告诉大家6大设计原则和23中设计模式规范代码,却没有告诉大家这些个玩意的实际操作,还有些例子就是动物-猫-狗之类的了。一直在使用现实的例子,去说明设计模式,为什么不能用实际问题去完成解释设计模式呢?继承真的很好,只适用于代码的复用,可以指定重写函数,但是不要抽象父类多态了好吗?为什么?要是继承玩多态,那么请问你继承,封装,多态,为什么会是三个特性啊,写成继承,封装不就好咯,反正继承可以玩多态啊。继承有点像组合,组合的缺点就是依赖和实例调用,写好继承一定要记住“拿来”, 总结继承一句话:继承不抽象,拿来不要还。

我:原来这才是继承啊,继承是一个动词,我一直当做名词了。伤心伤心,从一开始就是错的,走了太长的弯路了啊。开始学习的时候感觉继承好简单,后来加入抽象父类就不知道要干嘛了, 这让我对继承有了新的认识。学习的时候封装感觉也很简单,不知道封装是不是也被我们误用了呢?

OOP:封装,非常好理解,字面意思就是把代码装到一个盒子里。但是仍然好多同学写不好,我们来看看为什么。封装一个很重要的概念就是隐藏具体细节实现,只保留部分接口交互。写不好封装多数是对private/FilePrivate/Public/Open 这些访问权限的认知不足,对于代码的安全性没有意识。举个例子class 一个name属性,没有设置访问权限,那么谁都可以访问,甚至更改,那么其中一个业务流程对其改变,在另一个业务流程就会得到改变的数据(这是不希望发生的)。有人说封装太简单,我都没有出过问题,权限神马的不需要。我只想呵呵呵,当你出问题的时候再来看看你说的这句话。封装,这三个特性里面我认为是最不好写的,封装需要你要有一种代码安全意识,而这些不是一朝一夕就可以锻炼的。这需要你对代码了解的很深,源码也要了解,其他反编译技术等也要了解。说的有点猛,不过只要对语言了解的OK,经验足够,也可以写出不错的封装。(在这里要夸奖一下object-c,他需要.h,.m文件,从编写上就规范了封装的特性,只有h文件的可以外部访问)。封装时大家还会犯一个错误,接口参数太多了,我见过一个接口是个参数的,而且这些参数无章可循。内部实现一堆ifelse。这种不能忍我跟你说,多谢几个接口会屎吗?内部函数加个private会屎吗?改改,好不好。封装写不好的真的千奇百怪,总结一句:封装加权限,接口要简单。

我:我改,我一定改,你说的问题我都犯了。你一边说我变流冷汗,我好多封装只是代码的堆叠,完全没有封装的概念,像是在广场的代码,里边什么东西都可以看见。我要回去改代码了,太可怕了。

OOP:别走,代码一时半会改的完吗?现在走了,我讲了一半,回去改的时候还是一头雾水。

我:是我着急了,磨刀不误砍柴工。您请说。

OOP:多态,这是一个抽象的概念,是一个开始让人费解,明白之后发现好简单的东西。对于继承和封装,多态我感觉是比较简单的东西。有太多人使用抽象父类做多态才会bug频出,误以为这样才是多态的正确使用方式,前面也说了继承不是干这个的。那么多态的正确使用方式是什么呢?面向接口编程这个大家又听说啊,解耦利器(耦合也叫依赖),多态的正确使用方式就是接口(interface)。IOC(控制反转),AOP 面向切面编程,都有interface的存在。以MVC为例,在这里MVC虽然和好的解决了M和V的耦合依赖关系,但是M和C,V和C的依赖关系严重了,项目我们controller可能特别多,需求来了不管三七二十一先写个C。那么如何利用多态完成解耦呢?

错误的方式

class Contoller{

    let v = View() //依赖严重,controller无法复用
    let m = Model()
    m.load(callBack:{ (data) ->
        v.setup(data:data)
    })
}
class Model{
   func load(callBack:(String) -> ()){
        callBack("abc")
   }
}

class View{
  func setup(data:String){
     textView.text = data
  }
}

正确的方式

class Contoller{
    var m:ControllerInterface?
    init(m:ControllerInterface,v:View){  //初始化,Model,View的实例化不在C里面,解除了依赖关系
       v.setup(m:m)  
       self.m = m                    //Model和View只通过interface交互通讯,完全解耦
    }                               //model可以继承多个接口,可以完成更加复杂的操作,但是
                                    //除非是相关的接口,乱用可以会出事的,接口还要很多玩法自己试试吧
    func clickBack(){
          self.m.back()
    }
}
interface ControllerInterface{
    func back()
}
class Model:ViewInterface,ControllerInterface{
   func load(callBack:(String) -> ()){
        callBack("abc")
   }
   func back(){
      println("返回")
   }
}

class View{
   func setup(m:ViewInterface){
       m.load(callBack:{ (data) in
          textView.text =  data
       })
   }
}
interface ViewInterface{
   func load(callBack:(String) -> ())  //闭包
}

看完之后大家应该就会明白,多态的使用时多样的,变化很多。对于代码的改善是非常大的,同时模式和规范是统一的,大家学起来也会很方便和简单。多态的意义其实是一个类有多个状态,每个状态解决一个问题 ,这是为了解决相关联问题的方式。实践中很容易吧多态类写的臃肿,这是要注意的。 总结一句:多态不变态,接口不依赖

想要学好OOP,思想理解困难户,送你六句真言:继承不抽象,拿来不要还。封装加权限,接口要简单。多态不变态,接口不依赖。每日默念,必定成仙。

我:你好幽默,听你说完感觉好开心,难的东西多变的简单了。谢谢你的六句真言,我必定每日默念,加深理解。真的很感谢你。诶诶诶,仙人别走,仙人。。。。。。艹迟到了,怎么办?

地铁上响起”继承不抽象,拿来不要还。封装加权限,接口要简单。多态不变态,接口不依赖。“,我的天啊今天怎么了,哦,仙人的六句真言,开心。

转载自  wangyongyue 的知乎专栏

报歉!评论已关闭.