1. 策略模式
1. 定义
策略模式定义了算法族,分别封装起来,让它们之间可以相互替换,此模式让算法的变化独立于使用算法的客户。
2. 示例代码
每个鸭子都会引用实现QuackBehavior
接口的对象,鸭子对象不亲自处理呱呱叫行为,而是委托给quackBehavior
引用的对象
public class Duck {
QuackBehavior quackBehavior;
public void performQuack() {
quackBehavior.quack();
}
}
3. 优缺点
优点
- 算法可以自由切换
- 避免使用多重条件判断
- 扩展性良好
缺点
- 策略类会增多
2. 观察者模式
1. 定义
定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新。
2. 示例代码
被观察者可以通过registerObserver
和removeObserver
注册和移除观察自己的观察者,当自身发生变化时(setChanged
可增加附加条件),可以调用notifyObservers
通知观察者,调用观察者的update
方法
public interface Observerable {
public void registerObserver(Observer o);
public void removeObserver(Observer o);
public void notifyObservers();
public void setChanged();
}
public interface Observer {
public void update();
}
3. 优缺点
优点
- 观察者和可观察者之间用松耦合方式结合
- 建立的一套事件触发机制
缺点
- 如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃
- 观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化
3. 装饰者模式
1. 定义
动态地将责任附加到对象上,若要扩展功能,装饰者提供了比继承更有弹性地替代方案。
2. 示例代码
将对象传入beverage
装饰者构造函数中,返回一个包装后的新对象Mocha
,当调用原来的cost
方法时,新的价格被自动添加到原来的价格,因此可以多次传入到不同的装饰者中进行链式包装。
public class Mocha extends CondimentDecorator {
Beverage beverage;
public Mocha(Beverage beverage) {
this.beverage = beverage;
}
public double cost() {
return 0.2 + beverage.cost(); //装饰原来的cost方法
}
}
3. 优缺点
优点
- 装饰类和被装饰类可以独立发展,不会相互耦合,装饰模式是继承的一个替代模式
缺点
- 多层装饰增加了类的复杂程度
- 过多的装饰类会使程序变得很复杂
4. 工厂模式
1. 定义
定义了一个创建对象的接口,但由子类决定要实例化的类是哪一个。工厂方法让类的实例化推迟到子类。
2. 示例代码
public abstract class PizzaStore {
public Pizza orderPizza(String type) {
Pizza pizza;
pizza = createPizza(type);
return pizza;
}
//实例化披萨的责任被转移到一个方法中,此方法就如同一个工厂
protected abstract Pizza createPizza(String type);
}
public class NYPizzaStore extends PizzaStore{ //子类具体实现该方法
Pizza createPizza(String item) {
if (item.equals("cheese")) {
return new NYStyleCheesePizza();
} else {
return new NYStyleNormalPizza();
}
};
}
3. 优缺点
优点
- 一个调用者想创建一个对象,只要知道其名称就可以了
- 扩展性高,如果想增加一个产品,只要扩展一个工厂类就可以
- 屏蔽产品的具体实现,调用者只关心产品的接口
缺点
- 每次增加一个产品时,都需要增加一个具体类和对象实现工厂,使得系统中类的个数成倍增加
5. 抽象工厂模式
1. 定义
提供一个接口,用于创建相关或依赖对象的家族,而不需要明确指定具体类。
2. 示例代码
通过构造函数传入原料工厂,Pizza
类与区域原料之前解耦,可以轻松地复用。
public interface PizzaIngredientFactory { //定义一个原料工厂接口
public Dough createDough();
public Sauce createSauce();
public Cheese createCheese();
}
public class NYPizzaIngreFactory extends PizzaIngredientFactory { //使用工厂方法实例化具体工厂
public Dough createDough() {
return new ThinCrustDough();
}
public Sauce createSauce() {
....
}
...
}
public class CheesePizza extends Pizza {
PizzaIngredientFactory ingredientFactory;
public CheesePizza (PizzaIngredientFactory ingredientFactory) {
this.ingredientFactory = ingredientFactory; //将工厂传入构造函数参数存储起来
}
void prepare() {
dough = ingredientFactory.createDough();
sauce = ingredientFactory.createSauce();
cheese = ingredientFactory.createCheese();
}
}
3. 优缺点
优点
- 客户从具体的产品中解耦,利用抽象接口来创建一组相关的产品,而不关心实际产出的具体产品
缺点
- 产品族扩展非常困难,要增加一个系列的某一产品,既要在抽象的 Creator 里加代码,又要在具体的里面加代码
4. 工厂模式和抽象工厂模式的区别
- 工厂模式使用继承,而抽象工厂则通过对象的组合来创建对象
- 抽象工厂通过抽象的接口来创建一个产品家族,其将一群相关的产品集合起来,其内部经常使用工厂方法来实现具体工厂。
6. 单件模式
1. 定义
确保一个类只有一个实例,并提供一个全局访问点。
2. 示例代码
将构造器声明变为私有的,类外部只有运用getInstance()
方法实例化对象。getInstance()
方法中需要使用同步锁 synchronized
防止多线程同时进入造成 instance 被多次实例化
public class Singleton{
private static Singleton uniqueInstance;
private Singleton() {
public static Singleton getInstance() {
if (uniqueInstance == null) {
uniqueInstance = new Singleton();
}
return uniqueInstance;
}
}
}
3. 优缺点
优点
- 在内存里只有一个实例,减少了内存的开销,尤其是频繁的创建和销毁实例
- 避免对资源的多重占用(比如写文件操作)
缺点
- 没有接口,不能继承
7. 命令模式
1. 定义
将“请求”封装成对象,以便使用不同的请求、队列或者日志来参数化其他对象。命令模式也支持撤销的操作
2. 示例代码
命令对象提供一个execute
方法,这个方法封装了接收者的一些动作,调用这个方法就会调用这些动作。客户在调用者对象上调用setCommand
方法,并把它传入命令对象。命令对象被存储调用者对象中,等待调用者在某个时间调用命令对象的execute
方法。
public interface Command {
public void execute();
}
public class LightOnCommand implements Command { //亮灯操作
Light light;
public LightOnCommand(Light light) {
this.light = light;
}
public void execute() {
light.on();
}
public void undo() { //亮灯的撤销操作
light.off();
}
}
public class SimpleRemoteControl {
HashMap<String, Command> slot = new HashMap<String, Command>(); //将命令存储在hashmap中
Command preCommand; //这里为了简化功能,用一个变量存储上一次执行的命令,实际应用可用栈记录更多历史命令
public SimpleRemoteControl() {}
public void setCommand(Command command, String type) { //传入命令对象,并保存在调用对象内
slot.put(type, command);
}
public void buttonWasPressed(String type) { //调用命令对象的命令
preCommand = slot.get(type)
preCommand.execute();
}
public void undoButtonPressed() {
preCommand.undo();
}
}
3. 优缺点
优点
- 降低了系统耦合度
- 新的命令可以很容易添加到系统中去
缺点
- 增加了过多的命令类
8. 适配器模式
1. 定义
将一个类的接口,转换为客户期望的另一个接口。适配器让原本接口不兼容的类可以合作无间。适配器包含对象适配器和类适配器两种,其中类适配器通过多重继承的方法实现,其不需要重新实现整个被适配者。
2. 示例代码
下面包含鸭子和火鸡两个类,下面代码将火鸡包装成鸭子,使用户像使用鸭子一样来使用火鸡
public interface Duck {
public void quack();
public void fly();
}
public class MallardDuck implements Duck { //鸭子类
public void quack() {
System.out.println("Quack");
}
public void fly() {
System.out.println("I’m flying");
}
}
public interface Turkey {
public void gobble();
public void fly();
}
public class WildTurkey implements Turkey { //火鸡类
public void gobble() {
System.out.println("Gobble");
}
public void fly() {
System.out.println("I’m flying a short distance");
}
}
public class TurkeyAdapter implements Duck { //对象适配器。将火鸡包装成鸭子
Turkey turkey
public TurkeyAdapter(Turkey turkey) {
this.turkey = turkey;
}
public void quack() {
turkey.gobble();
}
public void fly() {
turkey.fly();
}
}
3. 优缺点
优点
- 提高了类的复用
- 灵活性好
缺点
- 过多地使用适配器,会让系统非常零乱,不易整体进行把握
9. 外观模式
1. 定义
提供一个统一的接口,用来访问子系统中的一群接口。外观定义了一个高层接口,让子系统更容易使用。
2. 示例代码
外观类通过组合的方式,将对象保存在自身内部,然后构造一个高层接口,提供给客户使用
public interface Shape {
void draw();
}
public class Rectangle implements Shape {
@Override
public void draw() {
System.out.println("Rectangle::draw()");
}
}
public class Square implements Shape {
@Override
public void draw() {
System.out.println("Square::draw()");
}
}
public class Circle implements Shape {
@Override
public void draw() {
System.out.println("Circle::draw()");
}
}
public class ShapeMaker {
private Shape circle;
private Shape rectangle;
private Shape square;
public ShapeMaker() {
circle = new Circle();
rectangle = new Rectangle();
square = new Square();
}
public void drawCircle(){
circle.draw();
}
public void drawRectangle(){
rectangle.draw();
}
public void drawSquare(){
square.draw();
}
}
3. 优缺点
优点
- 减少系统相互依赖
- 提高灵活性
- 提高了安全性
缺点
- 如果子系统经常变化,还需要修改外观类,比较麻烦
10. 模板方法模式
1. 定义
在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中实现。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤。
2. 示例代码
模板方法定义算法骨架,一些步骤交给子类继承实现,还可以通过钩子函数控制算法的流程
abstract class AbstractClass {
final void templateMethod() {
operation1();
operation2();
if (hook()) {
extraOperation();
}
finalOperation3();
}
abstract void operation1(); //由子类实现
abstract void operation2(); //由子类实现
final void finalOperation3() {
//一些实现
};
final void extraOperation() {
//一些额外步骤实现
};
void hook() { //钩子函数,子类可以覆盖决定是否增加算法步骤
return false;
}
}
3. 优缺点
优点
- 封装不变部分,扩展可变部分
- 提取公共代码,便于维护
- 行为由父类控制,子类实现
缺点
- 每一个不同的实现都需要一个子类,导致类的个数增加,使得系统更加庞大
4. 策略模式和模板方法模式的区别
- 模板方法中算法的个别步骤交给子类继承实现,算法总体结构仍在在父类控制,对父类具有依赖性,而策略模式通过对象组合来实现算法,后者灵活性更大。
11. 迭代器模式
1. 定义
提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露其内部的表示。
2. 示例代码
迭代器模式封装了原本的集合,并暴露统一的接口给外部客户,客户无法得知内部集合是数组还是列表。
public interface Iterator {
boolean hasNext();
Object next();
}
public class DinerMenuIterator implements Iterator {
MenuItem[] items;
int position = 0;
public DinerMenuIterator(MenuItem[] item) {
this.items = items;
}
public Object next() { //供外部客户调用
MenuItem menuItem = items[position];
position = position + 1;
return menuItem;
}
public boolean hasNext() { //供外部客户调用
if (position >= items.length || items[position] == null) {
return false
} else {
return true;
}
}
}
3. 优缺点
优点
- 访问一个聚合对象的内容而无须暴露它的内部表示
- 为遍历不同的聚合结构提供一个统一的接口,解耦客户和内部集合
缺点
- 由于迭代器模式将存储数据和遍历数据的职责分离,增加新的聚合类需要对应增加新的迭代器类
12. 组合模式
1. 定义
允许你将对象组合成树形结构来表现“整体/部分”层次结构。组合能让客户以一致的方式处理个别对象以及对象组合。
2. 示例代码
每个职员都是树中的节点,都为Employee
类型,其在内部维护一个subordinates
集合用于存放子节点。其用add
、remove
等方法来增添和移除子节点。在实际应用中,应考虑叶子节点和非叶子节点调用方法时的不同之处。组合模式采取迭代器进行遍历,如果遍历时进行的计算开销较大,还可以考虑在节点上缓存。
public class Employee {
private String name;
private String dept;
private int salary;
private List<Employee> subordinates;
//构造函数
public Employee(String name,String dept, int sal) {
this.name = name;
this.dept = dept;
this.salary = sal;
subordinates = new ArrayList<Employee>();
}
public void add(Employee e) {
subordinates.add(e);
}
public void remove(Employee e) {
subordinates.remove(e);
}
public List<Employee> getSubordinates(){
return subordinates;
}
public String toString(){
return ("Employee :[ Name : "+ name
+", dept : "+ dept + ", salary :"
+ salary+" ]");
}
}
3. 优缺点
优点
- 节点自由增加
- 组件的接口一致,用户用起来比较简单,无须关心对象的类型。
缺点
- 叶子节点和非叶子节点都是继承同一类型,违反了单一责任的设计原则
13. 状态模式
1. 定义
允许对象在内部状态改变时改变它的行为,对象看起来好像修改了它的类。
2. 示例代码
对象在内部保留多个状态类的引用,这些状态类具有相同的方法和属性,但内部具体实现逻辑不同,通过切换内部状态类,对象调用的方法也随之改变,外面看就像换了一个类一样。
public class StartState implements State {
public void start(Context context) {
}
public void close(Context context) {
context.setState(new CloseState()); //注意状态的切换
System.out.println("close State");
}
}
public class CloseState implements State {
public void start(Context context) {
context.setState(new StartState()); //注意状态的切换
System.out.println("start State");
}
public void close(Context context) {
}
}
public class Context {
private State state;
public void setState(State state) { //切换状态的方法
this.state = state;
}
public State getState() {
return state;
}
public void start() {
getState().start(this);
}
public void close() {
getState().close(this);
}
}
3. 优缺点
优点
- 减少了大量if语句
- 将所有与某个状态有关的行为放到一个类中,并且可以方便地增加新的状态,只需要改变对象状态即可改变对象的行为
- 可以让多个环境对象共享一个状态对象,从而减少系统中对象的个数
缺点
- 状态模式的使用必然会增加系统类和对象的个数
4. 策略模式和状态模式的区别
状态模式对象会随着时间而改变状态,改变行为是状态模式的核心。策略模式虽然也可以在运行时改变组合,但其主要是控制对象使用什么策略,而状态模式改变是自动的,无意识的。
14. 代理模式
1. 定义
为另一个对象提供一个替身或占位符以控制对这个对象的访问。包括远程代理控制访问远程对象、虚拟代理控制访问创建开销大的资源、保护代理基于权限控制对资源的访问。
2. 示例代码
下面是一个加载图片的虚拟代理实现,其代理在第一次访问时从硬盘加载图片,并将图片保存在内存中,后续访问内存中的对象。
public interface Image {
void display();
}
public class RealImage implements Image {
private String fileName;
public RealImage(String fileName){
this.fileName = fileName;
loadFromDisk(fileName);
}
@Override
public void display() {
System.out.println("Displaying " + fileName);
}
private void loadFromDisk(String fileName){
System.out.println("Loading " + fileName);
}
}
public class ProxyImage implements Image{ //图片代理
private RealImage realImage;
private String fileName;
public ProxyImage(String fileName){
this.fileName = fileName;
}
@Override
public void display() {
if(realImage == null){ //第一次访问需要加载图片
realImage = new RealImage(fileName);
}
realImage.display(); //后续访问直接使用缓存对象
}
}
3. 优缺点
优点
- 职责清晰
- 扩展性强
- 安全性高
缺点
- 由于在客户端和真实主题之间增加了代理对象,因此有些类型的代理模式可能会造成请求的处理速度变慢
4. 装饰者模式和代理模式的区别
装饰者模式强调为对象增加行为,可以进行多次包装。代理模式不仅给对象加上行为,其本身代表对象,当原对象还未创建时,代码对象甚至担当原对象的身份,且很少会用多个代理进行嵌套包装。
15. MVC
1. 定义
MVC 模式代表 Model-View-Controller(模型-视图-控制器) 模式。这种模式用于应用程序的分层开发。其是多个设计模式结合起来的模式
2. 概念
- 视图: 用来呈现模型。视图通常直接从模型中取得它需要显示的状态和数据。用户与视图进行交互,视图会告诉控制器用户的操作。
- 模型: 模型持有所有的数据、状态和程序逻辑。模型不关注视图和控制器,虽然它提供了操作和检索状态的接口,并发送状态改变给观察者。视图和控制器都可以成为模型的观察者,接收模型变化的通知。
- 控制器: 取得用户的输入并解读其对模型的意思。控制器也可以直接改变视图。
3. 内含模式
- 视图和控制器实现了策略模式,视图是一个对象,可以被调整使用不同的策略,而控制器提供了策略,视图只关心界面可视部分,对于任何界面行为,都委托给控制器处理。
- 视图包括了窗口、面板、按钮、文本等标签。每个显示组件都是一个节点,应用了组合模式。
- 模型实现了观察者模式,当状态发生改变时,会通知视图或控制器。
4. MVVM
MVVM(Model-View-ViewModel)是MVC的变形,它采用双向绑定(data-binding):View的变动,自动反映在 ViewModel,反之亦然。相比MVC模式,当视图需要更新时,无须自己编写控制器的代码改变视图,而视图有交互时,也无须编写控制代码处理视图的交互操作。
http://www.ruanyifeng.com/blog/2015/02/mvcmvp_mvvm.html