写在前面
设计模式代表了最佳的实践,是众多软件开发前辈经过相当长一段时间的试验和总结出来的理念,是一套被反复使用、经过分门别类的一套问题解决方案。合理的使用设计模式能够保证代码可靠性,让代码更容易被他人理解。足以见得学习设计模式是每一个程序猿的必修课。
给设计模式分个类
设计模式有两种分类方法,也就是根据模式的目的来分和根据模式作用的范围来分。
-
根据模式的目的来分,可以分为创建型模式、结构型- 模式和行为型模式三种。
- 创建型模式,描述怎么创建对象,主要特点是将对象的创建和使用剥离,例如单例、工厂模式。
- 结构型模式,描述如何将类或对象布局成更大的结构,例如代理、适配器模式。
- 行为型模式,描述类或对象怎么相互协作完成任务,例如观察者、访问者模式。
-
根据模式的作用范围来分,可以分为类模式和对象模式两种。
- 类模式,处理类与子类之间的(继承)关系,例如工厂、适配器、模板方法、解释器模式。
- 对象模式,处理对象之间的关系,除了以上四种之外的所有模式都是对象模式。
1.“寂寞孤独”的单例模式
在JAVA中,每个类都可能拥有多个实例,就像每个人最终都会有多个孩子一样,当然也许你不想生孩子,嫌奶粉钱太贵,这个时候你就可以选择孤独一生——同样的,JAVA中的类也有这种选择,这个时候就要用到单例模式啦。
所谓的单例模式……也就是一个类在jvm虚拟机中只有一个实例。要达成这个需要三个要素。
- 1.构造方法私有化。
- 2.静态属性指向实例。
- 3.public static getInstance方法,返回第二步的静态属性。
尝试使用这三个元素:
public class Singleton {
//1.私有化构造
private Singleton(){}
//2.静态属性指向实例
private static Singleton instance = new Singleton();
//3.public static方法,提供静态实例对象
public static Singleton getInstance(){
return instance;
}
}
上面的就是一个典型的单例模式啦,也许你注意到了在这个例子中一旦类加载就会创建实例,就像饿死鬼一样,因此我们叫他饿汉式单例。我们在这个的基础上做一些变化:
public class Singleton {
//1.私有化构造
private Singleton(){}
//2.静态属性指向实例
private static Singleton instance = null;
//3.public static方法,提供静态实例对象
public static Singleton getInstance(){
if (instance == null){
return instance = new Singleton();
}
return instance;
}
}
在这个例子中,只有在调用getinstance方法时才会创建实例,懒得动都不想动,要你催他他才不情不愿地创建,这可不就是懒汉嘛。
至于为什么要区分饿汉和懒汉……可能在某些情况下,比如资源较多时,这样加载的时间就会很长……为了避免这样的情况,就可以使用懒汉式了。
另外,如果涉及到线程安全的问题的话,在getInstance方法前加上synchronized就可以啦。
至于用途,很多地方需要有且只有一个实例的时候,就可以使用单例模式了,比如每个人的身份证号、一个班的班长时……
2.工厂模式(普通工厂)
工厂模式是JAVA中最常用的设计模式之一,属于创建型模式,提供了一种创建对象的最佳方式。
在这个模式中,我们创建对象时不会对客户端暴露创建逻辑,而是通过一个共同的接口来指定新创建的对象,就仿佛一个庞大的工厂,嗯,一个创建对象的工厂。
通过实例来感受一下工厂模式↓
/**假如这个工厂是一个汽车厂,首先要有一个汽车接口,里面有一个run方法*/
public interface Car {
void run();
}
/**奔驰车类实现了汽车接口*/
class Benz implements Car {
@Override
public void run() {
System.out.println("嘟嘟嘟,奔驰车开动啦");
}
}
/**宝马车类实现了汽车接口*/
public class Bmw implements Car {
@Override
public void run() {
System.out.println("嘟嘟嘟,宝马车启动啦");
}
}
/**创建汽车的工厂*/
public class CarFactory {
public Car getCar(String carType){
if (carType == null){
return null;
}
if (carType.equalsIgnoreCase("Benz")){
return new Benz();
}
if (carType.equalsIgnoreCase("Bmw")){
return new Bmw();
}
return null;
}
}
/**最后测试,就能通过工厂(CarFactory)来创建汽车(对象)啦*/
public class TestFactory {
public static void main(String[] args) {
CarFactory carFactory = new CarFactory();
Car benz = carFactory.getCar("Benz");
Car bmw = carFactory.getCar("Bmw");
benz.run();
bmw.run();
}
}
通过上面的例子大概能够感受什么叫做工厂模式了,下面来总结一下优缺点:
- 优点是只需要对象名就能调用对象、扩展性高、调用者不需要知道实现方式,只要关注接口就行了。
- 缺点是每次增加一个产品时,就要增加一个具体类和工厂,比如这次是汽车工厂,下次没准就是牙刷工厂、啤酒工厂……显而易见的,代码量和系统复杂度大大增加。
3.抽象工厂模式(超级工厂)
前面的工厂模式考虑的是单一的产品,比如汽车……比如电视,而在抽象工厂模式中,工厂却变成了一个综合形大工厂。
一样的通过实例来感受:
/**一样的汽车接口和汽车实现类*/
public interface Car {...}
class Benz implements Car {...}
public class Bmw implements Car {...}
/**加上另一个需求:游戏机的接口和实现类*/
public interface GameBoy {
void play();
}
public class PS4 implements GameBoy {
@Override
public void play() {
System.out.println("我要打PS4游戏");
}
}
public class XBOX implements GameBoy {
@Override
public void play() {
System.out.println("我要打XBOX游戏!");
}
}
按照工厂模式的套路,这里要开始创建工厂了,有两个需求就要创建两个工厂,然后调用的时候需要自己创建工厂对象,这样显然不符合工厂模式的理念,那么……看来我们需要一个创建工厂的工厂↓
/**和汽车、游戏机的接口一样,也需要一个工厂接口*/
public abstract class AbstractFactory {
public abstract Car getCar(String carType);
public abstract GameBoy getGameBoy(String gameBoyType);
}
package com.designpattern.factroypattern;
/**和汽车工厂一样,只不过这是一个创建工厂的工厂*/
public class Factory2Factory {
public static AbstractFactory getFactory(String factoryType){
if (factoryType.equalsIgnoreCase("Car")){
return new CarFactory();
}
if (factoryType.equalsIgnoreCase("GameBoy")){
return new GameBoyFactory();
}
return null;
}
}
/**测试的时候先创建工厂,再创建产品*/
public class TestFactory {
public static void main(String[] args) {
AbstractFactory carFactory = Factory2Factory.getFactory("Car");
Car benz = carFactory.getCar("Benz");
benz.run();
AbstractFactory gameBoyFactory = Factory2Factory.getFactory("GameBoy");
GameBoy ps4 = gameBoyFactory.getGameBoy("PS4");
ps4.play();
}
}
所谓的抽象工厂模式,就是对工厂模式的一种变形,只是增加了一个制造工厂的工厂。
- 优点:既然是工厂模式的变形,当然满足工厂模式的所有优点,此外,还能满足客户端在同一时刻只使用一个工厂产出的对象———如果是用工厂模式来实现这样的需求,那就有问题了。
- 缺点:如果新增一个工厂类别,需要修改虚拟工厂、实现工厂……所有的工厂类都需要改变,想想都觉得蛋疼。
4.装饰器模式(待更新)
……
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。