面向对象设计(1)

来源:互联网 发布:淘宝维护要多久 编辑:程序博客网 时间:2024/05/22 00:30

基石

  • OOD是用客观现实(真实世界)的规律和方式去告诉计算机做什么(编程)。

从需求开始谈起

  • 需求总是不完整的,错误的,容易让人产生误解
  • 需求一直在变化
    • 用户对需求的看法,可能在与开发人员讨论以及看到软件新的可能性后发生变化
    • 随着对问题的熟悉, 开发人员对问题领域的看法也会变化
    • 不是需求在变, 而是人们对需求的理解在变化
  • 如何去应对变化?

例子一:听课

假设你在一个会议上担当讲师, 听课的人在课后还要去听其他课程, 但他们不知道下一堂课的听课地点,
你的责任就是, 确保大家都知道下一节课去哪儿上。

  • 结构化的分析方法
    这里写图片描述
  • 你很有可能会这么做
    这里写图片描述

到底谁来负责?

  • 责任的转移
    • 第一种方法: 直接给每个人提供指示, 责任全在老师自己身上
    • 第二种方法:只给出通用的指示(调用接口), 让每个人自己去完成任务 (面向对象的思路), 责任在各个学生身上
  • 让责任划分到合适的对象当中!

面向对象的好处

  • 老师只需要对Student发出一个“笼统”的指令:gotoNextClassroom() 即可, 不用关心实现细节

  • 思考: 假设出现了需求变更, 你讲的课有研究生作为助教, 它们需要把本节课的反馈收集一下,先交到会议办公室,然后再去下一个教室。 对于结构化设计和OO 怎么应对?

应对变更

  • 对于结构化设计, 不得不对控制程序进行修改, 加上if else 判断以区分研究生和普通学生, 给研究生以特殊指令 – 修改控制程序,容易产生bug

  • 对于OOD , 根本不区分研究生和普通学生, 只是对Student这个抽象概念说: gotoNextClassRoom()

for (Student student : students){    student.gotoNextClassRoom()}//Student 可能是普通学生,也可能是研究生助教

总结一下

  • 职责转移

    • 把职责划分到合适的类中去
    • 把细节封装起来
  • 只对接口进行操作

    • gotoNextClassroom() !

问题

  • 等等 ! 你讲的例子不就是封装和多态吗? 我已经掌握了OO的三大特性:封装、继承、多态 ,学会了定义类,封装属性, 我还会定义类的继承体系, 这难道还不够吗?

  • 为什么还要学习面向对象的设计? 先看示例二

例子二:选课

  • 科目(Course): ID, 描述, 时长,最多学生数, 先修科目

  • 学生(Student): ID, 姓名, 已修科目

  • 课程(CourseOffering): 科目, 上课地点, 老师,选这门课的学生

public class Course {    private String id;    private String desc;//课目描述    private int duration;//课目时长    List< Course > prerequisites;//先修科目    public List<Course> getPrerequisites () {        return prerequisites;    }    public boolean equals( Object o ){        if( o == null || !( o instanceof Course ) ) {            return false;        }        Course c = ( Course ) o;        return ( c != null ) && c.id.equals( id );    }}public class Student {    private String id;    private String name;//名字    private List< Course > coursesAlreadyTaken = new ArrayList< Course >();//已经上过的课程    public List< Course > getCoursesAlreadyTaken() {        return coursesAlreadyTaken;    }}public class CourseOffering {    private Course course;//上课课目    private String location;//上课地点    private String teacher;//上课老师    private int maxStudents;    List< Student > students =  new ArrayList< Student >();//学生    public int getMaxStudents() {        return maxStudents;    }    public List< Student > getStudents() {        return students;    }     public Course getCourse() {        return course;    }}

业务代码:
这里写图片描述

这段代码如果不加注释读起来相当费尽,所以需要重构。有些责任不应该在这里做。

开始重构:

这里写图片描述

这里写图片描述

这里写图片描述

  • 思路更加清晰

例子三:《Head First 设计模式》模拟鸭子游戏

这里写图片描述


主管要求加上飞行的行为

这里写图片描述


可怕的问题发生了, 一个橡皮鸭也能飞行了! 看看是怎么回事

这里写图片描述


解决办法: 把fly()方法也给覆盖了

这里写图片描述

向父类添加一个方法,导致很多子类需要改动,这是继承给我们带来的好处吗? 这还是继承吗?!


可是将来加入一个诱饵鸭该怎么办, 诱饵鸭是木头假鸭,不会飞也不会叫
这里写图片描述

是不是受够方法覆盖了!! !


换个思路: 用接口怎么样?
这里写图片描述


面向对象设计的准则

  • 发现变化并且封装变化
    • 找出可变之处, 把它独立出来,不要和那些不需要变化的代码混在一起。
    • 一个抽象的过程。
  • 针对接口编程而不是针对实现编程

  • 优先使用组合而不是继承


发现变化并且封装变化

  • 鸭子类的fly()和quack()会随着鸭子的不同而改变

这里写图片描述

呱呱叫的行为

这里写图片描述

针对接口编程

这里写图片描述

这里写图片描述

总结一下

  • 面向对象的三大特性:封装、继承、多态 只是提供了一种工具, 如何使用这样的工具才真正的考验码农的功力。

  • 日常的工作主要是在一个框架下填充代码, 很少有机会去提升OOD的能力。

  • OOD 是写类库,框架等软件的必备技能

软件开发的不同视角

  • 概念层次上,对象是一组责任

    • 这个对象要负责什么?
  • 规约层次上,对象是一组可以被其他对象或者自己调用的方法

  • 实现层次上, 对象是代码和数据

一个好的面向对象系统:

  • 面向对象的,可以复用的

  • 可以用最小的代价去修改系统

  • 不用修改(或者很少的修改)现有代码就可以扩展

面向对象设计的原则

  • 单一职责原则 (SRP)
  • 开闭原则 (OCP)
  • Liskov 替换原则 (LSP)
  • 接口隔离原则 (ISP)
  • 依赖倒置原则 (DIP)

  • SOLID:写出优雅代码的关键所在

PS:上面的每个原则将会后续发文

练习

public class UserSettingService{    public void changeEmail(User user)  {        if(checkAccess(user))  {               //改变EMail                }    }    public boolean checkAccess(User user) {        //验证用户是否有效    }}

重构后

public class UserSettingService{    public void changeEmail(User user)  {        if(SecurityService.checkAccess(user))    {        //改变EMail        }    }}public class SecurityService{    public static boolean checkAccess(User user)  {        //检查访问权限    }}//真的要这么做拆分吗? 

结束语言:站在更高的层次看自己的代码

原创粉丝点击