https://mp.weixin.qq.com/s?__biz=MzUzNzAzMTc3MA==&mid=2247484148&idx=1&sn=eb480cd6ba5bdf2579b37e63b8837d79&chksm=faec67c1cd9beed7701de2e8a8c0896f57e0f617fbd6544ec6ce74e16c9e08fcf63e170b2964&scene=178&cur_album_id=1908992469812199431#rd

简介

最近思考了一下这几年写的业务,都是用面向过程思路来实现。之所以用面向过程
一是因为大家都这么写,很少去思考是否有更好的实现
二是业务简单,使用面向过程编程可以很方便的实现
三是业务需要快速上线,面向过程的方法更快更直接
但是弊端也很明显,随着业务不断的积累,项目改动起来比较困难,需要不断梳理前人是怎么写的、有什么影响,测试需要做大量的测试,确保功能是正常的。有时候即使是相似或者迭代性的需求也无法保证快速上线。而且即使耗费了大量的人力,也很难保证没有遗漏。这时候就想起面向对象的好。

四大特性

面向对象编程有四大特性

  1. 封装特性
    封装也叫作信息隐藏或者数据访问保护。类通过暴露有限的访问接口,授权外部仅能通过类提供的方式来访问内部信息或者数据。它需要编程语言提供权限访问控制语法来支持,例如Java中的private、protected、public关键字。封装特性存在的意义,一方面是保护数据不被随意修改,提高代码的可维护性;另一方面是仅暴露有限的必要接口,提高类的易用性。
  2. 抽象特性
    封装主要讲如何隐藏信息、保护数据,那抽象就是讲如何隐藏方法的具体实现,让使用者只需要关心方法提供了哪些功能,不需要知道这些功能是如何实现的。抽象可以通过接口类或者抽象类来实现,但也并不需要特殊的语法机制来支持。抽象存在的意义,一方面是提高代码的可扩展性、维护性,修改实现不需要改变定义,减少代码的改动范围;另一方面,它也是处理复杂系统的有效手段,能有效地过滤掉不必要关注的信息。
  3. 继承特性
    继承是用来表示类之间的is-a关系,分为两种模式:单继承和多继承。单继承表示一个子类只继承一个父类,多继承表示一个子类可以继承多个父类。为了实现继承这个特性,编程语言需要提供特殊的语法机制来支持。继承主要是用来解决代码复用的问题。
  4. 多态特性
    多态是指子类可以替换父类,在实际的代码运行过程中,调用子类的方法实现。多态这种特性也需要编程语言提供特殊的语法机制来实现,比如继承、接口类、duck-typing。多态可以提高代码的扩展性和复用性,是很多设计模式、设计原则、编程技巧的代码实现基础。

    实现

    想要用面向对象编程来实现业务逻辑,必然需要掌握的是如何使用这门语言实现四大特性。在掌握了这份知识的基础上,才有可能灵活的使用设计模式,设计出好的架构。今后学习新的语言的时候,对于面向对象的实现,需要成为关注点之一。看下面的内容需要掌握基本的Go语言知识。

    封装

    Go中封装的实现方法为使用结构体,并给结构体添加相应的方法。
    一般的实现步骤如下:

  5. 将结构体、字段的首字母小写;

  6. 给结构体所在的包提供一个工厂模式的函数,首字母大写,类似一个构造函数;
  7. 提供一个首字母大写的Set方法(类似其它语言的public),用于对属性判断并赋值;
  8. 提供一个首字母大写的Get方法(类似其它语言的public),用于获取属性的值。

举例:对于员工,不能随便查看年龄,工资等隐私,并对输入的年龄进行合理的验证。代码结构如下

  1. package model
  2. import "fmt"
  3. type person struct {
  4. Name string
  5. age int //其它包不能直接访问..
  6. sal float64
  7. }
  8. //写一个工厂模式的函数,相当于构造函数
  9. func NewPerson(name string) *person {
  10. return &person{
  11. Name: name,
  12. }
  13. }
  14. //为了访问age 和 sal 我们编写一对SetXxx的方法和GetXxx的方法
  15. func (p *person) SetAge(age int) {
  16. if age > 0 && age < 150 {
  17. p.age = age
  18. } else {
  19. fmt.Println("年龄范围不正确..")
  20. //给程序员给一个默认值
  21. }
  22. }
  23. func (p *person) GetAge() int {
  24. return p.age
  25. }
  26. func (p *person) SetSal(sal float64) {
  27. if sal >= 3000 && sal <= 30000 {
  28. p.sal = sal
  29. } else {
  30. fmt.Println("薪水范围不正确..")
  31. }
  32. }
  33. func (p *person) GetSal() float64 {
  34. return p.sal
  35. }


/
关于这个代码,有两点需要说明

  1. 关于大小写
    • 小写字母开头的函数只在本包内可见,大写字母开头的函数才能被其他包使用。这个规则也适用于类型和变量的可见性。
    • 大小写影响了可见性,大写字母开头等同于public,小写字母开头等同于private,这种做法不仅免除了public、private关键字,更重要的是统一了命名风格。
  2. 结构体方法参数
    • 如果要求对象必须以指针传递,这有时会是个额外成本,因为对象有时很小(比如4字节),用指针传递并不划算。
    • 只有在你需要修改对象的时候,才必须用指针。

      继承

      Go语言根本就不支持面向对象思想中的继承语法。
      从另一个维度而言,Go语言也提供了继承,但是采用了组合的文法,所以我们将其称为匿名组合。
      举例:

/

  1. package main
  2. import "fmt"
  3. type Base struct {
  4. Name string
  5. }
  6. func (b *Base) SetName(name string) {
  7. b.Name = name
  8. }
  9. func (b *Base) GetName() string {
  10. return b.Name
  11. }
  12. // 组合,实现继承
  13. type Child struct {
  14. base Base // 这里保存的是Base类型
  15. }
  16. // 重写GetName方法
  17. func (c *Child) GetName() string {
  18. c.base.SetName("modify...")
  19. return c.base.GetName()
  20. }
  21. // 实现继承,但需要外部提供一个Base的实例
  22. type Child2 struct {
  23. base *Base // 这里是指针
  24. }
  25. //
  26. type Child3 struct {
  27. Base
  28. }
  29. type Child4 struct {
  30. *Base
  31. }
  32. func main() {
  33. c := new(Child)
  34. c.base.SetName("world")
  35. fmt.Println(c.GetName())
  36. c2 := new(Child2)
  37. c2.base = new(Base) // 因为Child2里面的Base是指针类型,所以必须提供一个Base的实例
  38. c2.base.SetName("ccc")
  39. fmt.Println(c2.base.GetName())
  40. c3 := new(Child3)
  41. c3.SetName("1111")
  42. fmt.Println(c3.GetName())
  43. c4 := new(Child4)
  44. c4.Base = new(Base)
  45. c4.SetName("2222")
  46. fmt.Println(c4.GetName())
  47. }

关于这段代码,有几点需要说明

  1. 可以使用匿名组合和非匿名组合,如果使用非匿名组合,则调用的时候需要显示指定变量名称。如果使用匿名组合,则无需显示指定变量名称,当然也可以显示调用,如c3.Base.Name。
  2. 在Go语言中,还可以以指针方式从一个类型“派生”:如Child4,这段Go代码仍然有“派生”的效果,只是Child4创建实例的时候,需要外部提供一个Base类实例的指针。
  3. 在“派生类”Child3没有改写“基类”Base的成员方法时,相应的方法就被“继承”,例如在上面的例子中,调用c3.GetName()和调用c3.Base.GetName()效果一致。在“派生类”Child改写“基类”Base的成员方法,c.GetName()会调用派生类的方法,如果想调用基类的方法,可以显示调用。
  4. 如果“派生类”和“基类”有相同的变量名Name,所有的派生类的Name成员的访问都只会访问到最外层的那个Name变量,基类的Name变量相当于被覆盖了,可以用显示引用。

    多态

    在Go语言中,一个类只需要实现了接口要求的所有函数,我们就说这个类实现了该接口。如果类实现了接口,便可将对象实例赋值给接口。
    举例:

    1. package main
    2. import "fmt"
    3. type Money interface {
    4. show() string
    5. }
    6. type OldMoney struct {
    7. }
    8. func (oldMoney *OldMoney) show() string {
    9. return "I am old money"
    10. }
    11. type NewMoney struct {
    12. }
    13. func (newMoney *NewMoney) show() string {
    14. return "I am new money"
    15. }
    16. func PrintMoney(l []Money) {
    17. for _, item := range l {
    18. fmt.Println(item.show())
    19. }
    20. }
    21. func main() {
    22. moneyList := []Money{new(OldMoney), new(NewMoney), new(OldMoney)}
    23. PrintMoney(moneyList)
    24. }


    /
    关于这段代码,有几点需要说明:

  5. 接口赋值并不要求两个接口必须等价。如果接口A的方法列表是接口B的方法列表的子集,那么接口B可以赋值给接口A。

  6. Go语言可以根据下面的函数:func (oldMoney OldMoney) show() string自动生成一个新的show()方法:func (oldMoney *OldMoney) show() string,所以赋值时使用引用还是对象,需要考虑函数参数的类型。

    结论

    简单梳理了一下Go语言的特性,使用Go做面向对象编程及其简单。后面会用Go实现23种设计模式。

    资料

  7. Go语言中 封装的理解和实现细节 (Golang经典编程案例)https://blog.csdn.net/cui_yonghua/article/details/94346590

  8. golang继承与接口https://www.cnblogs.com/ExMan/p/11773843.html
  9. golang继承https://studygolang.com/articles/12114
  10. Golang组合和匿名的区别https://blog.csdn.net/wangkai_123456/article/details/107894993
  11. 二十七.golang的多态

最后

大家如果喜欢我的文章,可以关注我的公众号(程序员麻辣烫)
我的个人博客为:https://shidawuhen.github.io/
1. 设计模式(1)-语法 - 图1
技术

  1. MySQL开发规范
  2. HTTPS配置实战
  3. Go通道实现原理
  4. Go定时器实现原理
  5. HTTPS连接过程
  6. 限流实现2
  7. 秒杀系统
  8. 分布式系统与一致性协议
  9. 微服务之服务框架和注册中心
  10. Beego框架使用
  11. 浅谈微服务
  12. TCP性能优化
  13. 限流实现1
  14. Redis实现分布式锁
  15. Golang源码BUG追查
  16. 事务原子性、一致性、持久性的实现原理
  17. CDN请求过程详解
  18. 常用缓存技巧
  19. 如何高效对接第三方支付
  20. Gin框架简洁版
  21. InnoDB锁与事务简析
  22. 算法总结

读书笔记

  1. 原则
  2. 资治通鉴
  3. 敏捷革命
  4. 如何锻炼自己的记忆力
  5. 简单的逻辑学-读后感
  6. 热风-读后感
  7. 论语-读后感
  8. 孙子兵法-读后感

思考

  1. 服务端团队假期值班方案
  2. 项目流程管理
  3. 对项目管理的一些看法
  4. 对产品经理的一些思考
  5. 关于程序员职业发展的思考
  6. 关于代码review的思考
  7. Markdown编辑器推荐-typora