书籍豆瓣链接:《Head First设计模式》
开始学习时间:5-24
预计完成时间:6-9
实际完成时间:6-6
设计基础
-
继承
通过扩展一个已有的类,并继承改类的属性和行为,来创建一个新的类
-
封装
封装就是把抽象的数据和对数据进行的操作封装在一起,数据被保存在内部,程序的其他部分只有通过被授权的操作(成员方法)才能对数据进行操作。
-
多态
在判断引用对象的实际类型,根据其实际的类型调用相应的方法。分为静多态(编译时多态)和动多态(运行时多态)
-
抽象
定义一个类的时候,实际上就是把一类事物的公有的属性和行为提取出来,形成一个物理模型
-
抽象类和接口
【抽象类】 抽象方法只作声明、而不包含实现 不能被实例化 接口不能被实例化 接口只能包含方法声明 具体派生类必须覆盖基类的抽象方法 抽象派生类可以覆盖基类的抽象方法,如果不覆盖,则其具体派生类必须覆盖它们 【接口】 接口不能被实例化 接口只能包含方法声明 接口的成员包括方法、属性、索引器和事件 接口中不能包含常量、字段(域)、构造函数、析构函数、静态成员 【相同点】 都可以被继承 都不能被实例化 都可以包含方法声明 派生类必须实现未实现的方法 【不同点】 抽象类可以有构造方法,接口中不能有构造方法 抽象类中可以有普通成员变量,接口中没有普通成员变量 抽象类中可以包含静态方法,接口中不能包含静态方法 一个类可以实现多个接口,但只能继承一个抽象类 接口可以被多重实现,抽象类只能被单一继承 抽象类实现接口,可以把接口中方法映射到抽象类中作为抽象方法,而不是实现
UML类图
-
泛化关系
继承类和接口,java中通过extends实现
-
实现关系
实现接口,java中使用implements表示
-
依赖关系
是一种非常弱、临时性的关系。java中表现为,局部变量、方法中的参数和静态方法调用
-
关联关系
java中使用成员变量实现,或者使用全局变量
-
聚合关系
整体和部分弱依赖,部分生命周期不依赖整体,比如指针成员变量
-
组合关系
整体和部分强依赖,部分生命周期依赖整体,如普通成员变量
设计原则
-
将应用中可能变化之处,把他们独立出来
将会变化的部分取出并封装起来,以便以后可以轻易地改动或扩充此部分,而不影响不需要变化的其他部分
-
针对接口编程,而不是针对实现编程
通过接口来调用一个类及其方法,而不是直接建立类实体来调用类
-
多用组合,少用继承
组合的意思就是把你需要的东西组合在一个类里面,这个类并不需要继承任何父类,也可以提供想要的行为方法。利用继承设计子类的行为,是在编译时静态决定的,而且所有的子类都会继承到相同的行为。利用组合(composition)和委托(delegation)的做法扩展对象的可以在运行时动态地进行扩展。
-
为交互对象之间的松耦合设计而努力
松耦合的设计之所以能让我们建立有弹性的OO系统,能够应对变化,是因为对象之间的互相依赖降到了最低
-
开放-关闭原则 类应该对扩展开发,对修改封闭
我们的目标是允许类容易扩展,在不修改现有代码的情况下,就可搭配新的行为。这样的设计具有弹性可以应对改变,可以接受新的功能来应对改变的需求
-
依赖倒置原则
要依赖抽象,不要依赖具体的类 PizzaStore是个高层组件,Pizza是低层组件,不能让高层组件依赖底层组件,两者都应该依赖抽象
-
“最少知识”原则
减少对象之间的交互 就任何对象而言,在该对象的方法内,我们应该调用属于以下范围的方法: 该对象本身 被当做方法的参数而传递进来的对象(鸭子模型) 此方法所创建的或实例化的任何对象 对象的任何组件(HAS-A)
-
好莱坞原则
别调用我们,我们会调用你们 高层组件调用低层组件,低层组件绝对不可以直接调用高层组件
-
单一责任原则
一个类或者模块应该有且只有一个改变的原因 当一个模板或一个类被设计成只支持一组相关的功能时,我们说它具有高内聚,反之,当被设计成一组不相关的功能时,我们说它具有低内聚。“内聚”是一个比单一责任原则更普遍的概念,但两者其实关系很密切,遵守这个原则的类很容易具有高内聚,比背负很多责任的低内聚类更容易维护
设计模式
-
策略模式
定义算法族,分别封装起来,让它们之间可以相互替换,让算法的变化独立于使用算法的客户
-
观察者模式
定义了对象之间的一对多依赖,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新
-
装饰者模式
继承的问题:类数量爆炸,设计死板,以及基类加入的新功能并不适用于所有的子类 装饰器动态地将责任附加到对象上。若要扩展功能,装饰者提供了比继承更有弹性的替代方案 利用继承达到“类型匹配”,而不是利用继承获得“行为”,行为来自装饰者和基础组件,或与其他装饰者之间的组合关系
-
工厂方法模式
定义了一个创建对象的接口,但由子类决定要实例化的类是哪一个,工厂方法让类把实例化推迟到子类。 简单工厂把全部的事情在一个地方处理完了,工厂方法确实创建一个框架,让子类决定要如何实现。比如说,在工厂方法中,orderPizza方法提供了一般的框架,以便创建比萨,orderPizza方法依赖工厂方法创建具体的类,并制造出实际的比萨。简单工厂的做法,可以将对象的创建封装起来,但是简单工厂不具备工厂方法的弹性,不能变更正在创建的产品。 【简单工厂模式】 使用一个工厂类来创建产品,客户端避免了直接创建产品对象的责任 优点: 简单工厂包含必要的判断逻辑,简单工厂实现了对象的创建和使用的分离 客户端无需知道所创建的具体产品类的类名,只需要具体产品类对应的引用 在不修改任何客户端代码的情况下更换和增加新的具体产品类 缺点: 工厂类的职责过重,一旦出问题,整个系统都要出问题 在添加新的类的时候,简单工厂类就要修改,违反了开放——封闭原则 简单工厂的静态方法,使得工厂角色无法形成基于继承的等级结构 【工厂方法模式】 优点: 增加修改新类不用修改代码,只需要增加对应的工厂就好,完全符合开放——封闭性原则 创建对象的细节完全封装在具体工厂内部,具体工厂都继承了自己的父类,完美的体现了多态性 缺点: 增加新的产品,需要增加新的工厂类,会带来额外的开销 抽象层的加入使得理解程度加大
-
抽象工厂模式
抽象工厂提供一个接口,用于创建相关或依赖对象的家族,而不需要明确指定具体类 在工厂方法模式中,我们的具体创建者每次使用都只能创建一个同类型的对象 假如我们现在需要的是多个不同类型的对象,工厂方法就满足不了需求了 这时我们可以把多个工厂方法组合到一个类,这就是抽象工厂模式 它就是专门用来创建多个产品,也可以说是创建产品家族的
-
单件模式
确保一个类只有一个实例,并提供一个全局访问点 【双重检查加锁】 创建一个对象的过程是这样的: memory = allocate() # 分配对象的内存空间 ctorInstance(memory) # 初始化对象 instance = memory # 设置instance指向刚分配的内存地址 编译器和处理器有可能为了优化程序性能而对指令序列进行重排序 memory = allocate() # 分配对象的内存空间 ctorInstance(memory) # 初始化对象 instance = memory # 设置instance指向刚分配的内存地址 多线程情况下,其他线程会持有一个还未初始化的对象,volatile变量禁止重排序
-
命令模式
命令模式是为了解决命令的请求者和命令的实现者之间的耦合关系。将“请求”封装成对象,以便使用不同的请求,队列或者日志来参数化其他对象。 抽象命令接口Command: 定义命令的统一接口 public interface Command{ void execute(); } 具体的命令对象ConcreteCommand: Command接口的实现者,用来执行具体的命令 public class LightOnCommand implements Command{ private Light light; public LightOnCommand(Light light){ this.light = light; } public void execute(){ light.lightOn(); } } 传递命令对象Invoker: 命令的请求/请求者,对各个命令进行组装控制 public class XiaoAi{ private Command command; public void setCommand(Command command){ this.command = command; } public void doCommand(){ command.execute(); } } 接受者对象Receiver: 命令的实际执行者 public class Light{ public void lightOn(){ System.out.println("灯打开了!"); } } 客户端对象Client: 创建具体命令的对象并且设置命令对象的接受者 public class Client{ public static void main(string[] args){ // 创建Invoker XiaoAi xiaoAi = new XiaoAi(); // 创建Receiver Light light = new Light(); // 创建ConcreteCommand LightOnCommand lightOnCommand = new LightOnCommand(light) // Invoker接受Command xiaoAi.setCommand(lightOnCommand); // Invoker执行Command xiaoAi.doCommand(); } }
-
适配器模式
将一个类的接口,转换成客户期望的另一个接口,适配器让原本接口不兼容的类可以合作无间 适配器分为:类适配器和对象适配器 对象适配器:使用对象组合,以修改的接口包装被适配者,被适配者的任何子类,都可以搭配着适配者使用 类适配器:类适配器不是使用组合来适配被适配者,而是继承被适配者和目标类 适配器、外观、装饰者和代理模式的区别: 适配器模式:包装另一个对象,并提供不同的接口。 外观模式:包装许多对象,以简化他们的接口。 装饰者模式:包装另一个对象,并提供额外的行为。 代理模式:包装另一个对象,并控制对它的访问。
-
外观模式
提供了一个统一的接口,用来访问子系统中的一群接口,外观定义了一个高层接口,让子系统更容易使用 外观模式的目的是不是给予子系统添加新的功能接口,而是为了让外部减少与子系统内多个模块的交互,松散耦合,从而让外部能够更简单地使用子系统 外观模式的本质是:封装交互,简化调用
-
模板方法模式
模板方法模式在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法的某些步骤。 钩子是一种被声明在抽象类中的方法,但只要空的或者默认的实现。钩子的存在,可以让子类有能力对算法的不同点进行挂钩。 模板方法、策略和工厂方法 模板方法,子类在不改变算法结构的情况下,重新定义算法的某些步骤 策略,子类决定如何实现算法,算法步骤不要求相同 工厂方法,具体工厂类决定如何实例化哪个具体产品类
-
迭代器模式
提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露其内部的表示 迭代器提供统一的接口,针对接口编程,而不针对实现编程
-
组合模式
允许你将对象组合成树形结构来表现“整体/部分”层次结构。组合能让客户以一致的方式处理个别对象以及对象组合 模式对比: 策略 封装可以互换的行为,并使用委托决定使用哪一个 适配器 改变一个或多个类的接口 迭代器 提供一个方式来遍历集合,而无须暴露集合的实现 外观 简化一群类的接口 组合 客户可以将对象的集合以及个别的对象一视同仁 观察者 当某个状态改变时,允许一群对象能被通知到
-
状态模式
定义一个state接口,在这个接口内,糖果机的每个动作都有一个对应的方法 为机器中的每个状态实现状态类,这些类将负责在对应的状态下进行机器的行为 将动作委托到状态类 当状态转换是固定的时候,适合放在context中;转换更动态的时候,通常会放在状态类中
-
代理模式
为其他对象提供一种代理以控制对这个对象的访问,在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用 抽象角色:通过接口或抽象类生命 代理角色: 真实角色: RMI(remote method invocation, 远程方法调用),客户辅助对象成为stub(桩),服务辅助对象成为skeleton(骨架) skeleton对数据进行序列化,stub对数据进行反序列化
-
复合模式
模型-视图-控制器(model view controll, MVC) 模型:实现了观察者模式,当状态改变时,可以让模型完全独立于视图和控制器。同一个模型可以使用不同的视图,同一模型可以使用不同的视图 视图:显示包括了窗口、面板、按钮、文本标签等。每个显示组件不是组合节点,就是叶节点。当控制器告诉视图更新时,只需要告诉视图最顶层的组件,组合会处理其余的事。 控制器:视图和控制器实现了策略模式,视图是一个对象,可以被调整使用不同的策略,而控制器提供了策略。
其他模式
留到看《设计模式》再看
-
桥接模式
-
生成器
-
责任链
-
蝇量模式
-
解释器
-
中介者
-
备忘录
-
原型
-
访问者