浅析Java设计模式(一)

设计模式可以分为三大类
1.创建型模式 这些设计模式提供了一种在创建对象的同时隐藏创建逻辑的方式,而不是使用 new 运算符直接实例化对象。这使得程序在判断针对某个给定实例需要创建哪些对象时更加灵活。该类设计模式有五种:

  • 简单工厂模式(Factory Pattern)
  • 建造者模式(Builder Pattern)
  • 单例模式(SingletonPattern)
  • 原型模式(Prototype Pattern)
  • 抽象工厂模式(Abstract Factory Pattern)

2.结构型模式 这些设计模式关注类和对象的组合。继承的概念被用来组合接口和定义组合对象获得新功能的方式。该类模式有五种:

  • 代理模式(Proxy Pattern)
  • 装饰器模式(Decorator Pattern)
  • 适配器模式(Adapter Pattern)
  • 组合模式(Composite Pattern)
  • 桥接模式(Bridge Pattern)
  • 外观模式(FacadePattern)
  • 过滤器模式(Filter、CriteriaPattern)

3.行为型模式 这些设计模式特别关注对象之间的通信。

  • 观察者模式(Observer Pattern)
  • 中介者模式(Mediator Pattern)
  • 策略模式(Strategy Pattern)
  • 责任链模式(Chain of Responsibility Pattern)
  • 命令模式(Command Pattern)
  • 解释器模式(Interpreter Pattern)
  • 迭代器模式(Iterator Pattern)
  • 备忘录模式(Memento Pattern)
  • 状态模式(State Pattern)
  • 空对象模式(Null Object Pattern)
  • 模板模式(Template Pattern)
  • 访问者模式(Visitor Pattern)
    《浅析Java设计模式》将分为三篇分别对三类设计模式中比较常用且典型的几种模式做讲解。本篇文章是第一篇将从创建型模式中挑选简单工厂模式、单例模式以及建造者模式来解析。

一、简单工厂模式

简单工厂模式就是建立一个工厂类,对实现了同一接口的一些类进行实例的创建。简单工厂模式的实质是由一个工厂类根据传入的参数,动态决定应该创建哪一个产品类(这些产品类继承自一个父类或接口)的实例。
简单工厂模式UML
我们以画矩形、三角形以及圆为例,对于三种图形,可以抽象出来一个Shape,它们有共同的draw方法。并且让三种图形均实现Shape接口。简单工厂模式UML图如下所示:
这里写图片描述
1.首先定义Shape接口,接口中有三种图形共有的draw方法:

1
2
3
public interface Shape {
void draw();
}

2.创建三种形状并实现Shape接口:
矩形类:

1
2
3
4
5
6
public class Rectangle implements Shape {
@Override
public void draw() {
System.out.println("Draw a Rectangle");
}
}

三角形:

1
2
3
4
5
6
public class Triangle implements Shape {
@Override
public void draw() {
System.out.println("Draw a Triangle");
}
}

圆形:

1
2
3
4
5
6
public class Circle implements Shape {
@Override
public void draw() {
System.out.println("Draw a circle");
}
}

3.创建工厂类,并根据参数信息画出对应的图形:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class ShapFactory {
//使用 getShape 方法获取形状类型的对象
public Shape getShape(String shapeType){
if(shapeType == null){
return null;
}
if(shapeType.equals("Circle")){
return new Circle();
} else if(shapeType.equals("Rectangle")){
return new Rectangle();
} else if(shapeType.equals("Triangle")){
return new Triangle();
}
return null;
}
}

4.接下来,我们创建一个测试类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class FactoryTest {

public static void main(String[] args) {
ShapFactory shapFactory = new ShapFactory();

Shape shapeCircle = shapFactory.getShape("Circle");
shapeCircle.draw();

Shape shapeRectangle = shapFactory.getShape("Rectangle");
shapeRectangle.draw();

Shape shapeTriangle = shapFactory.getShape("Triangle");
shapeTriangle.draw();

Shape shapeSquare = shapFactory.getShape("Square");
if (shapeSquare == null) {
System.out.println("shapeSquare is null!");
} else {
shapeSquare.draw();
}
}
}

输出结果:

Draw a circle
Draw a Rectangle
Draw a Triangle
shapeSquare isnull!

二、单例模式

单例模式(Singleton Pattern),保证一个类仅有一个实例,并提供一个访问它的全局访问方法。单例模式需要我们把显示定义的构造方法私有化,不允许外部对它实例化。单例模式的结构图如下所示。

这里写图片描述
1.一个简单的单例模式实现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class Singleton {
private static Singleton instance;

private Singleton() {
}

public static Singleton getInstance() {
if(instance==null) {
instance = new Singleton();
}
return instance;
}

public void openWindow() {
System.out.println("Open a Window");
}
}

上面就是一个最简单的单例模式,我们来写一个测试类:

1
2
3
4
5
6
7
public class SingleTest {
public static void main(String[] args) {
Singleton single1=Singleton.getInstance();
Singleton single2=Singleton.getInstance();
System.out.println("single1.equals(single2)---"+single1.equals(single2));
}
}

输出结果:

single1.equals(single2)—true

从输出结果可以看出两次调用getInstance得到的是同一个Singleton 实例。

上面仅仅是实现单例的一种方式,在调用时实例化对象我们称之为懒汉式,上面的代码有一个弊端,就是不支持多线程调用,在多线程调用的情况下可能会被实例化出多个实例,因此,严格来讲它并不是真正的单例模式!接下来我们继续来看几种单例模式的实现方法,并且来保证它的线程安全。
2.线程安全的懒汉式单例模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Singleton {
private static Singleton instance;

private Singleton() {}

public static synchronized Singleton getInstance() {
if(instance==null) {
instance = new Singleton();
}
return instance;
}

public void openWindow() {
System.out.println("Open a Window");
}
}

如上代码,我们仅仅在getInstance方法上加了一个synchronized 锁,从而保证了线程的安全。但是我们又不得不考虑一个问题,即每次调用getInstance的时候都会加锁,这种做法势必影响了程序的性能,所以还有待改良。
3.双重锁定的单例模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class Singleton {
private static Singleton instance;

private Singleton() {
}

// 双重校验锁 线程安全 不用让线程每次都加锁,只有在未实例化时加锁,提高性能。
public static Singleton2 getInstance3() {
if (instance == null) {//先判断实例是否存在,不存在再加锁处理
synchronized (Singleton2.class) {
if(instance==null) {
instance=new Singleton2();
}
}
}
return instance;
}

public void openWindow() {
System.out.println("Open a Window");
}
}

上述代码中为什么两次判断instance是否为null?假如instance为null并且同时又两个线程调用getInstance()方法,它们将同时通过第一重instance==null的判断。然后由于锁机制,这两个线程只能有一个进入,另一个在外排队等候。等到第一个执行完后另一个线程才能进入。而此时如果没有第二重instance是否为空的判断,则第一个线程创建了实例,而第二个线程还是可以继续创建实例。这样没有达到单例的目的。
4.饿汉式实现单例模式
前三种我们讲解了懒汉式实现单例。那么还有一种在类加载时就创建对象的单例模式的实现方法我们称之为饿汉式。代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Singleton {
private static Singleton instance=new Singleton();

private Singleton() {
}

public static Singleton getInstance() {
return instance;
}

public void openWindow() {
System.out.println("Open a Window");
}
}

这种单例模式基于 classloader 机制避免了多线程的同步问题。但因为instance在类加载时就被实例化了,因此存在内存浪费的可能性。

三、建造者模式

建造者模式是将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
我们以画卡通人物为例,画一个卡通人物需要完成头、身体、手、腿的绘制。那么,我们就可以抽象出一个IDrawPerson的接口

1
2
3
4
5
6
7
8
9
10
11
12
public interface IDrawPerson {

void drawHead();

void drawBody();

void drawHand();

void drawLeg();

Person buildPerson();
}

接下来,我想画一个比较瘦的卡通人物,那么定义PersonThinBuilder并实现IDrawPerson类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
public class Person {
private String head;

private String body;

private String hand;

private String leg;

... //省去get、set方法
}


public class PersonThinBuilder implements IDrawPerson {
private Person mPerson;

public PersonThinBuilder() {
super();
mPerson=new Person();
}

@Override
public void drawHead() {
mPerson.setHead("draw head");

}

@Override
public void drawBody() {
mPerson.setBody("draw thin body");
}

@Override
public void drawHand() {
mPerson.setHand("draa hand");
}

@Override
public void drawLeg() {
mPerson.setLeg("draw leg");
}

@Override
public Person buildPerson() {
return mPerson;
}
}

同样,如果我想再画一个胖人或者高个子都用类似代码实现这个类就可以了。

接下来看建造者模式中很重要的一个类,指挥者类,用这个类来控制建造过程或者隔离用户与建造过程的关联。

1
2
3
4
5
6
7
8
9
public class PersonDirector {
public Person CreatePerson(IDrawPerson buildPerson) {
buildPerson.drawHead();
buildPerson.drawBody();
buildPerson.drawHand();
buildPerson.drawLeg();
return buildPerson.buildPerson();
}
}

最后来看创建一个建造者模式的测试类:

1
2
3
4
5
6
7
public class BuildTest {
public static void main(String[] args) {
PersonDirector personDirector=new PersonDirector();
Person thinPerson=personDirector.CreatePerson(new PersonThinBuilder());
System.out.println(thinPerson.getBody());
}
}

输出结果:

draw thin body


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!