计算机 · 2021年12月18日 0

Head First设计模式

OO基础

  • 抽象
  • 封装
  • 多态
  • 继承

OO原则

  • 找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化的代码混在一起。
  • 针对接口编程,而不是针对实现编程。
  • 多用组合,少用继承。
  • 为了交互对象之间的松耦合设计而努力。
  • 类应该对扩展开放,对修改关闭(开放-关闭原则)。
  • 依赖抽象,不要依赖具体类(依赖倒置原则)。
  • 好莱坞原则:别调用(call)我们,我们会调用(call)你。
    在好莱坞原则下,我们允许低层组件将自己挂钩到系统上,但是高层组件会决定什么时候和怎样使用这些低层组件。换句话说,高层组件对待低层组件的方式“别调用我们,我们会调用你”。
  • “最少知识”原则:只和你的密友谈话(减少对象之间的交互)。
    这个原则希望我们在设计中,不要让太多的类耦合在一起,免得修改系统中一部分,会影响到其他部分。如果许多类之间互相依赖,那么这个系统就会变成一个易碎的系统,它需要花许多成本维护,也会因为太复杂而不容易被其他人了解。
    这个原则与墨忒耳法则(Law of Demeter)是同一回事。
    这个原则的缺点是你可能为了遵循这个原则而造出很多用于包装的类。
  • 设计原则:一个类应该只有一个引起变化的原因。
    • 内聚(cohesion):用来衡量一个类或模块紧密地达到单一目的或责任。
      当一个模块或一个类被设计成只支持一组相关的功能时,我们说它具有高内聚;反之,当被设计成支持一组不相关的共能时,我们说它具有低内聚。内聚是一个比单一责任原则更普遍的概念,但两者其实关系是很密切的。遵守这个原则的类容易具有很高的凝聚力,而且比背负许多责任的低内聚类更容易维护。

OO模式

策略模式

  • 策略模式定义了算法族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化独立于使用算法的客户。

观察者模式

  • 观察者模式 定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新。

UML示意图:

装饰者模式

  • 装饰者模式 动态地将责任附加到对象上。若要扩展功能,装饰者提供了比继承更有弹性的替代方案。

UML示意图:

单例模式(Singleton Pattern)

单件模式确保一个类只有一个实例,并提供一个全局访问点。
对于多线程应用场景下的单例模式,可能会出现这样的问题:多个线程在要访问这个单例时,这个单例还没有实例化,此时存在着这个实例化过程的同步问题。可以考虑以下方案:

  • 使用synchronized关键字修饰getInstance方法
  • 在一开始就实例化这个单例而不是像惯例那样将其设为null
  • 双重检查加锁
    用volatile修饰单例,且只在检查单例为空时,需要实例化单例时才进行同步
    public class Singleton {
	private volatile static Singleton uniqueInstance;
	private Singleton() {}
	public static Singleton getInstance() {
	    if (uniqueInstance == null) {
		synchronized (Singleton.class) {
		    if (uniqueInstance == null) {
			uniqueInstance = new Singleton();
		    }
		}
	    }
	    return uniqueInstance;
	}
    }

简单工厂

并不是一个设计模式,而更像是一种编程习惯

public class PizzaStore {
	SimplePizzaFactory factory;

	public PizzaStore(SimplePizzaFactory factory) {
		this.factory = factory;
	}

	public Pizza orderPizza(String type) {
		Pizza pizza;

		pizza = factory.createPizza(type);

		pizza.prepare();
		pizza.bake();
		pizza.cut();
		pizza.box();
		return pizza;
	}

	...
}

public class SimplePizzaFactory {
	public Pizza createPizza(String type) {
		Pizza pizza = null;

		if (type.equals("cheese")) {
			pizza = new CheesePizza();
		} else if (type.equals("pepperoni")) {
			pizza = new PepperoniPizza();
		} else if (type.equals("clam")) {
			pizza = new ClamPizza();
		} else if (type.equals("veggie")) {
			pizza = new VeggiePizza();
		}

		return pizza;
	}
}

工厂模式

工厂模式定义了一个创建对象的接口,但由子类决定要实例化的类是哪一个。工厂方法让类把实例化推迟到子类。

抽象工厂模式

抽象工厂模式提供一个接口,用于创建相关或依赖对象的家族,而不需要明确指定具体类。

命令模式

命令模式将“请求”封装成对象,以便使用不同的请求、队列或者日志来参数化其他对象。命令模式也支持可撤销操作。

模板方法模式

模板方法模式在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤。

hook:如果你为原语操作提供一个默认的实现,那么这个原语操作就成为一个钩子(hook)。在你需要的时候可以在子类中重写这个hook方法。

适配器模式

适配器模式将一个类的接口,转换成客户期望的另一个接口。适配器让原本不兼容的类可以合作无间。

上面的uml图其实讲的是”对象”适配器,使用的组合的方式。另外还存在着“类”适配器,使用继承的方式。

对象适配器需要编程语言提供多重继承的支持。

外观模式(Facade Pattern)

外观模式提供了一个统一的接口,用来访问子系统中的一群接口。外观定义了一个高层接口,让子系统更容易使用。
简言之:隐藏细节,简化接口,让客户与低层组件解耦。

迭代器模式

迭代器模式提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露其内部的表示。

这里讲述的迭代器是“内部”迭代器,即我们在手动控制迭代器的移动(通过next操作),而“外部”迭代器则是类似于流计算那种我们告诉迭代器在取得每个元素时做什么事情的迭代器;

组合模式

组合模式允许你将对象组合成树形结构来表现“整体/部分”层次结构。组合能让客户以一致的方式处理个别对象以及对象组合。

一个组合迭代器,不仅遍历组件内的菜单项,而且确保所有的子菜单(以及子子菜单……)都被包含进来:

import java.util.*;

public class CompositeIterator implements Iterator {
	Stack stack = new Stack();

	public CompositeIterator(Iterator iterator) {
		stack.push(iterator);
	}

	public Object next() {
		if (hasNext()) {
			Iterator iterator = (Iterator) stack.peek();
			MenuComponent  component = (MenuComponent) iterator.next();
			if (component instanceof Menu) {
				stack.push(component.createIterator());
			}
			return component;
		} else {
			return null;
		}
	}

	public boolean hasNext() {
		if (stack.empty()) {
			return false;
		} else {
			Iterator iterator = (Iterator) stack.peek();
			if (!iterator.hasNext()) {
				stack.pop();
				return hasNext();
			} else {
				return true;
			}
		}
	}

	public void remove() {
		throw new UnsupportedOperationException();
	}
}

空迭代器(NullIterator):返回一个迭代器,而这个迭代器的hasNext()永远返回false。

import java.util.Iterator;

public class NullIterator implements Iterator {
	public Object next() {
		return null;
	}

	public boolean hasNext() {
		return false;
	}

	public void remove() {
		throw new UnsupportedOperationException();
	}
}

状态模式

状态模式允许对象在内部状态改变时改变它的行为,对象看起来好像修改了它的类。

代理模式

代理模式为另一个对象提供一个替身或占位符以控制对这个对象的访问。

结束

用书中附带的一个表来结束这篇笔记。主要是关于本书详细介绍的设计模式的简单总结,书中未详细介绍的设计模式就懒得看了,等以后有需要看所谓的四人帮写的那本介绍二十多种设计模式的书或者学spring时再来折腾设计模式吧。看了这本书应该算是对设计模式初步入门了。

模式描述
装饰者包装一个对象,以提供新的行为
状态封装了基于状态的行为,并使用委托在行为之间切换
迭代器在对象的集合之中游走,而不暴露集合的实现
外观简化一群类的接口
策略封装可以互换的行为,并使用委托来决定要使用哪一个
代理包装对象,以控制对此对象的访问
工厂方法由子类决定要创建的具体类是哪一个
适配器封装对象,并提供不同的接口
观察者让对象能够在状态改变时被通知
模板方法由子类决定如何实现一个算法中的步骤
组合客户用一致的方式处理对象集合和单个对象
单件确保有且只有一个对象被创建
抽象工厂允许客户创建对象的家族,而无需指定他们的具体类
命令封装请求成为对象