一、工厂方法模式
工厂方法模式适用于出现了大量产品需要创建,并且又具有相同的接口时,就可以使用工厂方法模式进行产品的创建。实现工厂模式的方式又分为三种:普通工厂方法模式、多个工厂方法模式、静态工厂方法模式。
- 普通工厂方法模式,建立一个工厂类,对实现了同一接口的一些类进行实例的创建。以信息发送为例,假设我们有短信发送和邮件发送两种方式,以下为代码实现:
Sender接口:
public interface Sender {
void send();
}
邮件发送和短信发送的具体实现类:
public class MailSender implements Sender{
public void send() {
System.out.println("mail sender");
}
}
public class SmsSender implements Sender{
public void send() {
System.out.println("sms sender");
}
}
工厂类:
public class SendFactory {
public Sender getSender(String type) {
if ("mail".equals(type)) {
return new MailSender();
} else if ("sms".equals(type)) {
return new SmsSender();
} else {
return null;
}
}
}
测试类:
public class FactoryTest {
public static void main(String[] args) {
SendFactory factory = new SendFactory();
Sender mailSender = factory.getSender("mail");
Sender smsSender = factory.getSender("sms");
mailSender.send();
smsSender.send();
}
}
运行结果:
mail sender
sms sender
- 多个工厂方法模式,普通方法模式中,通过传入标志判断的方式容易导致错误。多个工厂方法模式则是为每个类提供创建实例的方法。改进普通工厂方法的缺陷。
还以上面的消息发送为例,我们修改下工厂类:
public class SendFactory {
// 获取邮件发送实例
public Sender getMailSender() {
return new MailSender();
}
//获取短信发送实例
public Sender getSmsSender() {
return new SmsSender();
}
}
测试类相应改成调用这两个方法即可,比较简单易理解,就不列出了。
- 静态工厂方法模式,故名思意,该模式就是将多个工厂方法模式中的创建实例的方法改为静态的,这样就不需要创建工厂方法的实例进行调用。这里也不列出代码实现。该模式的应用有点像我们平时代码开发中的工具类。
二、抽象工厂模式
工厂方法模式会有一个问题,因为它实例的创建依赖工厂类,如果需要拓展,那么就要修改工厂类,违反开闭原则(对扩展开放,对修改关闭)。抽象工厂模式就可以解决该问题。
该模式是给每个类提供一个工厂方法,同时每个工厂方法类实现同一个工厂接口,UML图如下所示:
SmsSender、MailSender以及他们实现的接口Sender和上面的一致。接下来我们只给出工厂类的代码实例。
工厂接口:
public interface Provider {
Sender produce();
}
邮件和短信的工厂类:
public class SmsSendFactory implements Provider {
public Sender produce() {
return new SmsSender();
}
}
public class MailSendFactory implements Provider {
public Sender produce() {
return new MailSender();
}
}
创建实例只要调用不同工厂类的方法即可。如果我们需要增加一种消息方式(比如发微信)就无需修改现有的代码,只需要再增加一个对应的工厂类实现Provider接口,增加一个发送类实现Sender接口即可。不过感觉这种模式适合需要创建的产品总数不多的情况下,太多的话,就会有很多工厂类,感觉很冗余复杂。
三、单例模式
单例模式,即在一个jvm中,只有一个实例。单例模式一般分为饿汉式和懒汉式。
- 饿汉式,即在类加载的时候就创建实例,而不管实际是否需要。代码如下:
public class Singleton {
private static Singleton singleton = new Singleton();
private Singleton() {}
public static Singleton getSingleton() {
return singleton;
}
}
- 懒汉式,与饿汉式相对,实例只是在第一次被调用的时候才创建。懒汉式有许多种方法,下面给出其中一个线程安全的写法:
public class Singleton {
private static class init {
private static Singleton singleton = new Singleton();
}
private Singleton() {
}
public static Singleton getSingleton() {
return init.singleton;
}
}
单例模式除了线程安全,还需要考虑序列化和反序列化的问题,上面的例子中,如果不额外实现序列化的话,那么每反序列化一次,就会创建一个实例,这就不是单例了。同时也需要防止有人使用反射强行调用私有构造器。对于此,EffectiveJava一书中,有建议使用枚举来实现单例,代码如下:
public enum Singleton2 {
INSTANCE;
private String name;
public String getName(){
return name;
}
public void setName(String name){
this.name = name;
}
}
四、建造者模式
建造者模式和工厂模式有点不好区分,两者大体的区别是工厂模式关心的是一系列产品,而建造者模式关心的是单个产品的细节怎么构建。如车子,就工厂模式来说,它只关心各个品牌的车由哪些工厂建造,宝马由宝马工厂,奔驰由奔驰工厂。而建造者模式则会关心每个车子是由轮胎、发动机构成的。强调一个组成元素的构造过程。下面我们用造车子来进行代码实例展示。
汽车的蓝图:
public class CBlueprint {
private String wheel;
private String engine;
public String getWheel() {
return wheel;
}
public void setWheel(String wheel) {
this.wheel = wheel;
}
public String getEngine() {
return engine;
}
public void setEngine(String engine) {
this.engine = engine;
}
}
抽象构建者:
public interface CCarBuilder {
void buildWheel();
void buildEngine();
CBlueprint buildBlueprint();
}
宝马和奔驰的具体构建类:
public class BwmBuilder implements CCarBuilder {
private CBlueprint cBlueprint;
public BwmBuilder() {
cBlueprint = new CBlueprint();
}
public void buildWheel() {
System.out.println("build bwm wheel");
cBlueprint.setWheel("bwm wheel");
}
public void buildEngine() {
System.out.println("build bwm engine");
cBlueprint.setEngine("bwm engine");
}
public CBlueprint buildBlueprint() {
return cBlueprint;
}
}
public class BenzBuilder implements CCarBuilder {
private CBlueprint cBlueprint;
public BenzBuilder() {
cBlueprint = new CBlueprint();
}
public void buildWheel() {
System.out.println("build benz wheel");
cBlueprint.setWheel("benz wheel");
}
public void buildEngine() {
System.out.println("build benz engine");
cBlueprint.setEngine("benz engine");
}
public CBlueprint buildBlueprint() {
return cBlueprint;
}
}
最终建造者:
public class Director {
public CBlueprint buildCblueprint(CCarBuilder cCarBuilder) {
cCarBuilder.buildEngine();
cCarBuilder.buildWheel();
return cCarBuilder.buildBlueprint();
}
public static void main(String[] args) {
Director director = new Director();
director.buildCblueprint(new BwmBuilder());
director.buildCblueprint(new BenzBuilder());
}
}
五、原型模式
原型模式采取复制原型对象的方法来创建对象的实例。使用原型模式创建的实例,具有与原型一样的数据。
public class Person implements Cloneable {
@Override
protected Person clone() {
try {
return (Person) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
return null;
}
}
}
原型对象具体实现就是实现Cloneable接口,但是它没有实际的方法,只是对复制的一个标志。克隆还需要考虑浅复制和深复制。下面将举例说明:
浅复制:
public class Person implements Cloneable {
private String name;
private String sex;
private List list;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public List getList() {
return list;
}
public void setList(List list) {
this.list = list;
}
@Override
protected Person clone() {
try {
return (Person) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
return null;
}
}
}
深复制:
public class Person implements Cloneable {
private String name;
private String sex;
private List list;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public List getList() {
return list;
}
public void setList(List list) {
this.list = list;
}
@Override
protected Person clone() {
try {
Person person = (Person) super.clone();
List newList = new ArrayList();
for (String str : this.list) {
newList.add(str);
}
person.setList(newList);
return person;
} catch (CloneNotSupportedException e) {
e.printStackTrace();
return null;
}
}
}
说到复制,必须一提的是在EffectiveJava中,建议接口不要扩展(extend)Cloneable接口,为了继承而设计的类也不应该实现这个接口,具体原因可参见该书。