面向对象:封装 继承 多态
UML类图
第一行:
类名(斜体表示抽象类,<< interface >>表示接口)
第二行:字段或属性
第三行:方法 +表示public -表示private #表示protected
虚线空心箭头表示实现接口 - - - - - ▷
实线空心箭头表示继承类 ———-▷
实线箭头表示关联关系
,A类是B类的一个属性 B———>A
菱形箭头表示聚合关系
,例如雁群类
中有个属性是大雁[]
雁群 ◇——–> 大雁
还有依赖关系和组合关系
单一职责原则
SRP:
就一个类而言,应该仅有一个引起它变化的原因
在类的数量和单个类的职责之间求取平衡。
开放-封闭原则
对扩展开放,对更改封闭
在我们最初编写代码时,假设变化不会发生,当变化发生时,判断这种变化是否具有代表性,如果是,创建抽象来隔离以后发生的同类变化,尽量做到:面对需求,对程序的改动是通过增加代码进行的,而不是更改现有的代码
切记:对代码每个部分都刻意地抽象并不是好事
依赖倒转原则
针对接口编程,不要对实现编程
里氏代换原则
在任何地方,子类可以替换父类
因为有了里氏替换原则,才让依赖倒转成为可能,高层模块只需要依赖父类,扩展时只需要增加子类,由于子类刻意替换父类,所以高层模块无需修改。
三层架构
表示层
业务逻辑层
数据访问层(Data Access Layer)
迪米特法则
松耦合松耦合松耦合,重要的事情说三次
最小知识原则
简单工厂
一个接口有多种实现,或者一个类有多个子类,要根据条件创建不同的类的实例,将switch case语句放在简单工厂封装起来,具体的选择过程对客户端不可见,每次要创建实例只需调用简单工厂的创建方法,传入参数即可。
使用简单工厂前,客户端要知道所有的子类,自己做判断
使用简单工厂后,客户端只需要知道父类(由于里氏代换原则)和工厂类,不用自己做判断
策略模式
简单工厂只是封装了对实现类的选择过程,作为客户端,还是需要知道对象类(Bird)和工厂类(BirdFactory):
Bird bird = BirdFactory.getBird(birdType);
bird.fly();
如果可以把方法也让另一个类来调用就可以让客户端只知道一个类了,我们把BirdFactory
类名改成BirdContext
,直接在它里面增加一个属性Bird
,简单工厂里是把Bird
返回给客户端,客户端再用Bird
调用方法,那现在不要把Bird
返回回来,直接赋值给BirdContext
里面的字段,然后客户端直接调用BirdContext
的fly
方法,BirdContext
再调用属性Bird
的fly
方法,一切就大功告成了,对客户端又隐藏掉一个类。
上面说的这种方式其实是把策略模式和简单工厂做了结合,一般如果只是策略模式,就仅仅是封装方法
的话,客户端确实只知道一个类,但是还是要写条件语句做判断,有点像回到了还没使用简单工厂的情况
装饰模式
装饰模式在我们要装饰的类外面包一层(衣服),对程序来说,包一层意思就是把要装饰的类作为属性放到衣服里了,然后调用衣服的方法,衣服再去调用被装饰的类的方法,同时衣服本身再执行一些操作(否则这个装饰就没什么意义啦),而且既然是衣服,就要能嵌套的,有点像网络协议一层套一层的,或者是俄罗斯套娃??随着衣服一层层变多,方法也越来越丰满
这里需要注意,所有的衣服肯定是要继承一个父类的,毕竟它们都是衣服嘛,第一件衣服里面放的是肉体,第二件衣服里面放的是包着肉体的第一件衣服依次类推,这就要求肉体和衣服都是同源的,因为里氏替换原则,子类可以替换父类,它们就可以随意嵌套了
代理模式
代理模式其实是和装饰模式有点像,装饰类中保留着被装饰对象的引用,去调用被装饰对象的方法,代理类中也有被代理对象的引用,但他们的目的不一样,装饰模式是为了扩充被装饰类的功能,而代理模式单纯是为了隐藏被代理类,比如WebService使用代理对客户端隐藏了复杂的远程访问。
(尽管如此,还是觉得它们很像,哈哈哈哈哈)
工厂方法模式
这个模式主要是得跟简单工厂区分一下,简单工厂:
Bird有n个继承类:
Bird
Bird1 Bird2 Bird3 ... Birdn
BirdFactory
当我想创建3个Bird3:
Bird b = BirdFactory.getBird("3");
Bird bb = BirdFactory.getBird("3");
Bird bbb = BirdFactory.getBird("3");
工厂方法在简单工厂的基础上,给每一个类都建立一个工厂,而最终的BirdFacory就用来获得每个类自己的工厂:
Bird
Bird1 Bird2 Bird3 ... Birdn
Fac
Bird1Fac Bird2Fac Bird3Fac ... BirdnFac
BirdFactory
当我想创建3个Bird3:
Fac bird3fac = BirdFactory.getFac("3");
Bird b = bird3fac.getBird();
Bird bb = bird3fac.getBird();
Bird bbb = bird3fac.getBird();
区别出现了吧,"3"
少用了两次
原型模式
浅拷贝,深拷贝,不多说了
模板方法模式
把不变的部分挪到超类,去除子类的重复代码。
父类就是所谓的“模板”,模板中变化的部分用抽象方法让子类各自实现去。
比如项目中的BaseDao类,对每个表,sql语句都是一样的只是表名不同,所以在BaseDao里面提供一个getTableName
的抽象方法,每个表自己的dao类继承BaseDao并在getTableName
中提供自己的表名
外观模式
封装复杂逻辑,迪米特法则
很常用,很多时候用了却不自知
例如新旧系统整合,可以使用外观类封装旧系统逻辑,防止新旧系统过度耦合
建造者模式
用于构建复杂的对象,例如一个小人对象,有很多固定的组成部分,头,膀子,身子,腿等,不管是胖的瘦的高的矮的小人都有这些信息,为了防止遗漏,可以定义小人类
,包含抽象方法draw头,draw膀子,draw身子,draw腿
,实现类胖子
,瘦子
等只要继承小人类
便可确保实现这些方法,到这里还不够,因为虽然实现了方法,具体用的时候还是有可能忘记调用某个方法导致最后画出来的小人缺胳膊少腿的,因此还要有一个Director
类,把小人类
作为它的属性,它有一个固定不变的draw
方法,依次调用小人类
的各个方法,至此,把画小人的流程稳定下来了。
观察者模式
观察者模式适用于发布订阅场景。发布者发布一个消息,所有订阅者都要收到消息或作出某种响应,订阅者可能是各种不同的类,如果发布者自己维护订阅者列表去一个一个通知,可以想象改动会非常频繁,耦合性太强,此时就要用到观察者Observer
,观察者是夹在发布者和订阅者中间的类,发布者只负责发布消息并告诉观察者,观察者负责维护订阅者列表并发出通知,解除了发布者和订阅者的耦合,符合单一职责原则,只有消息发布需要修改时,再修改或扩展发布者。
书里这一块还提到了C#的代理Delegate
,以前写C#的时候没有太理解代理,现在豁然开朗。以观察者模式举例,假如所有的订阅者都实现了同一个订阅者接口,里面有固定的接收通知的方法receive()
,那么观察者的实现当然就是维护一个List<订阅者>
,当收到消息时只需要遍历订阅者依次调用receive()
就好了,但是实际中可能订阅者是各个不同的类,有的类是外部引用的,你不能去修改它,此时可以在观察者类中定义一个代理对象,一个代理对象可以绑定多个实例的指定方法,当执行代理对象时,它会依次调用自己身上绑定的方法,这种方式真是太灵活辣!可惜JAVA中没有这种用法
抽象工厂模式
就是扩充版本的工厂模式
. | UserDao | ProductDao | AnimalDao |
---|---|---|---|
Oracle | OracleUserDao | OracleProductDao | OracleAnimalDao |
Mysql | MysqlUserDao | MysqlProductDao | Mysql AnimalDao |
SqlServer | SqlUserDao | SqlProductDao | SqlAnimalDao |
之前说的简单工厂和工厂模式都只是针对其中一列而言
抽象工厂要负责创建一个系列中(Orcle,Mysql,SqlServer各表示一个系列)所有的对象,所以一个系列里有多少个Dao接口,每个系列的工厂里就有多少个创建Dao的方法(每个系列一个Factory,所有Factory继承同一个父类,工厂模式)。实际使用时,一般系列是指定好的不会动态修改的,所以考虑与配置文件和反射结合使用,减少系列变更时的修改量,并避免使用switch case语句。
状态模式
根据名称理解,一个类Person
有多种状态(所谓状态可能就是某个字段处于不同的值时,假如叫gender
),不同状态下某些方法的行为是不一致的(gender
为男,称呼先生,否则称呼女士),当状态不多(例如只有0或1)或是状态比较固定时,可以用简单的,也是我们会最先想到的if else
或是switch case
搞定,但是当状态很多,或是经常出现变动时,这个类就会不停修改,违背了开放-封闭原则
。面对这种情况,解决方案是建立一个状态接口或是抽象类,每种状态写成一个子类,把原来那个类中根据不同状态执行的不同代码分散到几个子类中,将这个接口作为一个属性放到原来的类里,类的状态转换就是对这个属性赋不同的类的值。调用状态类方法时将自身作为参数传进去即可,用来取属性判断是否处于当前状态,如果不属于,可以直接给主类的状态字段重新赋值。当要新增状态,或是修改某个状态的操作时,改动都很小,增加了可扩展性和可维护性。
适配器模式
要写一个接口(里氏替换原则),但是接口里的很多方法其实有一个类B
已经实现了(只是这个类没有实现我指定的接口),那怎么办呢重新实现一遍代价很大,此时可以使用适配器模式,将B
类作为一个属性放在新写的接口类里,在实现接口方法时调用B
里面的方法,相当于是做了一个适配器。