第一阶段 :方法和属性封装。面向对象是从面向过程发展来的,我们面向过程编写程序的时候,函数和属性是分开写的,这时候,参数的复用性较差,把属性和函数写在一起,属性不就可以复用了
第二阶段:方法和属性怎么封装和对象分层。这时候我们又发现了新的问题,一个对象(定义为低层对象)都在被另一个对象(定义为高层对象)使用。假定使用流程是,低层对象传入高层对象,高层对象处理低层对象,并且对低层对象的处理函数由高层对象实现。这样做有一个缺点,因为对低层对象的处理方式随着版本更新在不断变化,这时候就要不断修改高层对象。如何解决呢?将低层对象的处理方法由低层对象实现,高层对象只负责调用
以下h代表高层,l代表低层
第三阶段:抽象类。 类是由方法和属性组成的,本质还是方法的调用,那我们用方法代替类不就行了,依据这个思想出现了抽象类(只声明方法,但是不实现)。大家可以类比生活中的例子,我想要吃饭,只要说给我吃饭的工具就好了,至于是金筷子、银筷子还是木筷子,我并不指定,由提供餐具的老板去指定就好了
至此:对象被分为三层,分别是,高层、低层、抽象层
第四阶段:抽象类有啥用 一。 针对接口编程而不是针对实现编程。按照第二阶段编写程序,发现了新的问题,有时候写h类的时候,需要某个方法,这个方法是变化的模块,这时候我们可以在h类内只定义一个抽象类变量就可以了,l 层实现了,直接传进来就好
第五阶段:抽象类有啥用 二,针对接口编程而不是针对实现编程。这时又发现新问题,有个需求,h 层需要处理l 层的很多不同类型的对象,我们选择 I 层类的共有方法组成一个抽象类,这个抽象类定义的变量传入h类即可。
第六阶段:抽象类有啥用 三,针对接口编程而不是针对实现编程。如果l层的类实现了很多功能,这些功能有些是当前h层不需要的,完全没必要将I层对象传进来,在高层定义一个接口即可
总结:如果不采用抽象接口定义变量,高层就是高度耦合的,具体体现在,高层类和具体的低层类绑定,如果要使用别的类,高层就要加代码。并且不利于团队协作,假如高层不指定接口,低层屌丝不知道实现啥,可能是五花八门的
最后,重要的事情说三遍,针对接口编程而不是针对实现编程
高层和低层都是依赖抽象层,如下图。我们设计代码的时候也是这样,变化的模块从代码中抽离出一个抽象接口,由低层去实现。不断把变化的部分抽离成为一个个模块,使得低层的变化不会对高层产生影响,这就实现了下图。当然在程序编写之初,我们并不一定能明确的知道,哪个模块是经常变化的,也就抽象的不够好,这也就是代码重构的意义所在
总结:稳定(相对低层稳定)的代码写成非接口函数,变化的代码写成接口函数
我写了一个非常简单的标准的面向对象的三层代码:
从代码中可以看到,高层和低层都是依赖抽象层的。高层的稳定其实是由抽象层的稳定带来的
高层(相对低层稳定):
package high import "go-devops/design_model/inter" type High struct { arr []inter.Inter } func(h *High) Append(interDatas ...inter.Inter){ for _,single := range interDatas { h.arr = append(h.arr, single) } } func(h *High) Handle(){ for _,single := range h.arr { single.Eat() } }
抽象层(稳定):
package inter type Inter interface { Eat() }
低层(变化):
package low import "fmt" type People struct { Kind string Food string } func(p *People) Eat() { fmt.Printf("我是%v,我喜欢吃%v",p.Kind,p.Food) } type Dog struct { Kind string Food string } func(d *Dog) Eat() { fmt.Printf("我是%v,我喜欢吃%vn",d.Kind,d.Food) }
写代码测试下,能跑通:
package main import ( h "design_model/high" "design_model/low" ) func main() { dog := low.Dog{ Kind: "狗", Food: "屎", } people := low.People{ Kind: "人", Food: "肉", } high := h.High{} high.Append(&dog,&people) high.Handle() }
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)