设计模式-单例,原型与构建器

设计模式-单例,原型与构建器

创建型模式

创建型模式处理对象的初始化和克服构造函数的限制。

  • Singleton,
  • Builder,
  • Prototype,
  • Abstract Factory,
  • Factory pattern.

其他

  • other type of factories (like the static one),
  • pool pattern,
  • lazy initialization,
  • dependency injection,
  • service locator,

单例模式

我很少使用单例,因为它使代码很难进行单元测试,同时紧密耦合
我更倾向于使用工厂模式创建单例。

Ensure a class only has one instance, and provide a global point of access to it”

单例模式必须满足:

  • 只有一个实例
  • 可以从任何地方访问

singlton

example

1
2
3
4
5
6
7
8
9
10
11
12
public class SimpleSingleton {
//唯一实例,类加载时实例化。
private static final SimpleSingleton INSTANCE = new SimpleSingleton();
//禁止使用构造函数实例化对象
private SimpleSingleton() { }
//提供访问实例的方法
public static SimpleSingleton getInstance(){
return INSTANCE;
}
}

如果实例对象不使非常耗费内存,建议使用这种方法。(饿汉模式)
在多线程环境下(懒汉模式):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class TouchySingleton {
//保证内存可见性
private static volatile TouchySingleton instance;
private TouchySingleton() {
}
public static TouchySingleton getInstance() {
//因为lock很耗性能,所以同步之前先判断。双重锁
if (instance == null) {
synchronized (TouchySingleton.class) {
if (instance == null) {
instance = new TouchySingleton();
}
}
}
return instance;
}
}

什么时候使用单例

  • 只需要一个资源的时候(database connection, socket connection…)
  • 对于无状态的类,避免多个实例对象浪费内存。
  • 业务需要

你不应该使用单例作为共享变量,因为高耦合

你不应该使用单例:

  • 隐藏了类之间的依赖关系,而不是暴露出接口。
  • 违反了类设计的单一职责,单例控制了自己的创建和生命周期。同时又包含具体的功能。
  • 高耦合
  • 单元测试不友好,是代码难以预测。

使用单例代替单例模式

Spring框架的singleton并不是单例模式,而仅仅是单个实例。

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
48
49
50
51
52
53
54
////////////////An interface that represents a database connection
public interface DatabaseConnection {
public void executeQuerry(String sql);
}
////////////////A concrete implementation of this interface
////////////////In this example it's for a mysql database connection
public class MysqlDatabaseConnection implements DatabaseConnection {
public MysqlDatabaseConnection() {
// some stuff to create the database connection
}
public void executeQuerry(String sql) {
// some stuff to execute a SQL query
// on the database
}
}
////////////////Our business class that needs a connection
public class PersonBusiness {
DatabaseConnection connection;
//dependency injection using the constructor
// it is a singleton because the factory that
//creates a PersonBusiness object ensure that
//UniqueDatabaseConnection has only one instance
PersonBusiness(DatabaseConnection connection){
this.connection = connection;
}
//a method that uses the injected singleton
public void deletePerson(int id){
connection.executeQuerry("delete person where id="+id);
}
}
////////////////A factory that creates business classes
//////////////// with a unique MysqlDatabaseConnection
public class Factory {
private static MysqlDatabaseConnection databaseConnection = new MysqlDatabaseConnection();
public static MysqlDatabaseConnection getUniqueMysqlDatabaseConnection(){
return databaseConnection;
}
public static PersonBusiness createPersonBusiness(){
//we inject a MysqlDataConnection but we could chose
//another connection that implements the DatabaseConnection
//this is why this is a loose coupling
return new PersonBusiness(databaseConnection);
}
}

真实的例子

“Every Java application has a single instance of class Runtime that allows the application to interface with the environment in which the application is running. The current runtime can be obtained from the getRuntime method.”

  • java.lang.Runtime
  • java.awt.Toolkit

原型模式

原型模式通过拷贝对象实例取代通过构造函数创建实例对象。

“Specify the kinds of objects to create using a prototypical instance, and create new objects by copying this prototype.”

简言之,如果你不能使用构造函数创建对象,可以通过复制已经存在的实例对象创建。

UML
prototype

什么时候使用原型模式

  • 当系统不依赖于对象创建的细节。
  • 运行时动态创建。
  • 避免类的复杂的依赖关系。(现在可以通过依赖注入解决)
  • 复制比创建容易的时候。

example

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
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
//////////////////////////The Prototype interface
public interface Prototype {
Prototype duplicate();
}
//////////////////////////The class we want to duplicate
public class CarComparator implements Prototype{
private int priceWeigth;
private int speedWeigth;
private int fuelConsumptionWeigth;
//a constructor that makes costly calls to a database
//to get the default weigths
public CarComparator(DatabaseConnection connect){
//I let you imagine the costly calls to the database
}
//A private constructor only use to duplicate the object
private CarComparator(int priceWeigth,int speedWeigth,int fuelConsumptionWeigth){
this.priceWeigth=priceWeigth;
this.speedWeigth=speedWeigth;
this.fuelConsumptionWeigth=fuelConsumptionWeigth;
}
//The prototype method
@Override
public Prototype duplicate() {
return new CarComparator(priceWeigth, speedWeigth, fuelConsumptionWeigth);
}
int compareCars(Car first, Car second){
//some kickass and top secret algorithm using the weigths
}
////////////////The setters that lets the possibility to modify
//////////////// the algorithm behaviour
public void setPriceWeigth(int priceWeigth) {
this.priceWeigth = priceWeigth;
}
public void setSpeedWeigth(int speedWeigth) {
this.speedWeigth = speedWeigth;
}
public void setFuelConsumptionWeigth(int fuelConsumptionWeigth) {
this.fuelConsumptionWeigth = fuelConsumptionWeigth;
}
////////////////////////// A factory that creates a CarComparator instance using
////////////////////////// constructors then it creates the others by duplication.
////////////////////////// When a client ask for a CarComparator
////////////////////////// he gets a duplicate
public class CarComparatorFactory {
CarComparator carComparator;
public BusinessClass (DatabaseConnection connect) {
//We create one instance of CarComparator
carComparator = new CarComparator(connect);
}
//we duplicate the instance so that
//the duplicated instances can be modified
public CarComparator getCarComparator(){
return carComparator.duplicate();
}
}

真实的例子

Java interface Cloneable 提供一个clone()

1
2
3
4
5
6
7
8
9
10
11
// Let's initialize a list
// with 10 integers
ArrayList<Integer> list = new ArrayList<Integer>();
for (int i = 0; i < 10; i++) {
list.add(i);
}
System.out.println("content of the list "+list);
//Let's now duplicate the list using the prototype method
ArrayList<Integer> duplicatedSet = (ArrayList<Integer>) list.clone();
System.out.println("content of the duplicated list "+duplicatedSet);

构建器模式

构建器模式有助于分解复杂的代码。目的是避免创建大量的相似构造函数(只有部分参数不同)

“Separate the construction of a complex object from its representation so that the same construction process can create different representations.”

比如:

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
public class Person {
private int age;
private int weigth;
private int height;
private int id;
private String name;
//////////////Here comes the telescopic constructors
public Person() {
//some stuff
}
public Person(int age) {
this();//we're using the previous constructor
this.age = age;
}
public Person(int age, int weigth) {
this(age);//we're using the previous constructor
this.weigth = weigth;
}
public Person(int age, int weigth, int height) {
this(age, weigth);//we're using the previous constructor
this.height= height;
}
public Person(int age, int weigth, int height,int id) {
this(age, weigth, height);//we're using the previous constructor
this.id = id;
}
public Person(int age, int weigth, int height,int id,String name) {
this(age, weigth, height, id);//we're using the previous constructor
this.name = name;
}
}

1
2
3
Person person1 = new Person (45, 45, 160, 1);
Person person2 = new Person (45, 170, 150);

这样的代码非常冗长,难以阅读,很难使用。如果参数有120个,你是否愿意写120个构造函数?即使使用工厂模式,也需要120个工厂方法。
这就是构建器模式的优点。它模拟了其他语言(Python)的可选参数特性。

example

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
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
//////////////////////the person class
/////////////////////if you look at its constructor
/////////////////////it requieres a builder
public class Person {
private final int id;// mandatory
private int weigth;// optional
private int height;// optional
private int age;// optional
private String name;// optional
public Person(PersonBuilder builder) {
age = builder.age;
weigth = builder.weigth;
height = builder.height;
id = builder.id;
name = builder.name;
}
}
//////////////////////the builder that
/////////////////////takes care of
/////////////////////Person creation
public class PersonBuilder {
// Required parameters
final int id;
// Optional parameters - initialized to default values
int height;
int age;
int weigth;
String name = "";
public PersonBuilder(int id) {
this.id = id;
}
public PersonBuilder age(int val) {
age = val;
return this;
}
public PersonBuilder weigth(int val) {
weigth = val;
return this;
}
public PersonBuilder height(int val) {
height = val;
return this;
}
public PersonBuilder name(String val) {
name = val;
return this;
}
public Person build() {
return new Person(this);
}
}
//////////////////////Here is how to use the builder in order to build a Person
//////////////////////You can see how readable is the code
public class SomeClass {
public void someMethod(int id){
PersonBuilder pBuilder = new PersonBuilder(id);
Person robert = pBuilder.name("Robert").age(18).weigth(80).build();
//some stuff
}
public void someMethodBis(int id){
PersonBuilder pBuilder = new PersonBuilder(id);
Person jennifer = pBuilder.height(170).name("Jennifer").build();
//some stuff
}
}

另一个隐藏的技能:构建器模式可以创建不可变的对象。因为可以不提供setter方法,就没有办法通过构造函数修改对象的属性。

UML
builder

真实的例子

StringBuilder

1
2
3
4
StringBuilder sBuilder = new StringBuilder();
String example = sBuilder.append("this").append(" is")
.append(" an").append(" example").toString();
System.out.println(example);

© 2017 Hello World All Rights Reserved. 本站访客数人次 本站总访问量
Theme by hiero