一、设计模式的种类
设计模式比较常用的有23种,实际不止这些,我们统称为23种设计模式,其中常用的又只有其中的十几种。一般分为三类分别是创建型模式、结构型模式和行为型模式。
创建型模式有五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式和原型模式,其特点都是为创建对象提供抽象化得模板类,通过接口减少类之间的耦合。
结构型模式有七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、和享元模式,其特点是通过类之间的组合或继承结构在不修改原有类的基础上扩展类的功能。
行为型模式有十一种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。
二、设计模式的原则
1、开闭原则
开闭原则即对扩展开放对修改关闭,意思就是尽量不要修改已有的代码,通过扩展的方式来增加功能,这样就不需要对已有功能进行重复测试,避免破坏已有功能,减少开发的复杂性和耦合性。
2、里氏代换原则
里氏代换原则表示任何基类可以出现的地方,子类一定可以出现,即子类可以向上转型为基类而不会出现问题。这是继承复用的基石,只有当子类可以替换基类,软件功能不受影响时,基类才能真正被复用,子类也可以在基类的基础上增加新的行为。
基类与子类的这种关系正是抽象化的体现,不同子类通过各自实现基类的抽象化方法达到在不修改原有代码的基础上来扩展原有功能。因此可以说里氏代换原则是抽象化得具体实现,而抽象化是实现开闭原则的关键步骤。
3、接口隔离原则
接口隔离原则表示使用多个不相关的接口比使用单一接口要好,因为单一接口会导致耦合性过高。
4、依赖倒转原则
依赖倒转原则表示从依赖于具体实现类变为依赖于抽象化得接口,通过抽象化得接口获取需要的实例化对象,这样便于扩展功能而不需要修改原有代码,这是实现开闭原则的基础。
5、迪米特法则
迪米特法则即最少知道原则,从字面意思看就是和其他实体关联性越少越好,这也是一个降低耦合度的意思。
6、合成复用原则
尽量使用合成和聚合的方式扩展功能而减少使用继承的结构。因为继承会导致代码逻辑变得复杂,而组合的方式更能看清功能之前的关联关系,减少复杂性。
三、Java实现设计模式
1、工厂方法模式
首先是业务功能的功能接口
package com.terisadeng.creator.factory;
/**
* 生产接口
* @author SN
*
*/
public interface Productor {
public void produce();
}
然后是两个具体实现类
package com.terisadeng.creator.factory;
/**
* 生产实现类
* @author SN
*
*/
public class AudiProductor implements Productor{
@Override
public void produce() {
System.out.println("Produce Audi Car.");
}
}
package com.terisadeng.creator.factory;
/**
* 生产实现类
* @author SN
*
*/
public class BMWProductor implements Productor{
@Override
public void produce() {
System.out.println("Produce BMW car.");
}
}
然后是创建业务逻辑对象的工厂类
package com.terisadeng.creator.factory;
/**
* 工厂类
* @author SN
*
*/
public class ProduceFactory {
public static Productor produceBMW(){
return new BMWProductor();
}
public static Productor produceAudi(){
return new AudiProductor();
}
}
最后是测试工厂模式类
package com.terisadeng.creator.factory;
/**
* 测试工厂方法模式
* @author SN
*
*/
public class TestFactory {
public static void main(String[] args) {
Productor productor=ProduceFactory.produceBMW();
productor.produce();
productor=ProduceFactory.produceAudi();
productor.produce();
}
}
2、抽象工厂模式
抽象工厂模式是在工厂模式的基础上,将工厂类抽象化,避免扩展功能时修改工厂类,即通过定义工厂接口,不同的工厂实现类实现创建不同的业务逻辑对象。
增加工厂接口
package com.terisadeng.creator.abstractfactory;
import com.terisadeng.creator.factory.Productor;
/**
* 工厂接口
* @author SN
*
*/
public interface Factory {
public Productor produce();
}
两个工厂实现类,创建不同的业务逻辑对象
package com.terisadeng.creator.abstractfactory;
import com.terisadeng.creator.factory.AudiProductor;
import com.terisadeng.creator.factory.Productor;
/**
* 工厂实现类
* @author SN
*
*/
public class AudiFactory implements Factory{
@Override
public Productor produce() {
return new AudiProductor();
}
}
package com.terisadeng.creator.abstractfactory;
import com.terisadeng.creator.factory.BMWProductor;
import com.terisadeng.creator.factory.Productor;
/**
* 工厂实现类
* @author SN
*
*/
public class BMWFactory implements Factory{
@Override
public Productor produce() {
return new BMWProductor();
}
}
测试抽象工厂模式TestAbstractFactory.java
package com.terisadeng.creator.abstractfactory;
import com.terisadeng.creator.factory.Productor;
/**
* 测试抽象工厂模式
* @author SN
*
*/
public class TestAbstractFactory {
public static void main(String[] args) {
Factory factory=new BMWFactory();
Productor productor=factory.produce();
productor.produce();
}
}
3、单例模式
单例模式看起来简单,实现起来却有一定难度,原因在于多线程环境需要考虑线程安全问题。
首先是测试线程不安全的单例模式
package com.terisadeng.creator.singleton;
public class Singleton {
private static Singleton singleton=null;
//私有化构造函数,避免创建多个实例对象
private Singleton(){
}
public static Singleton getInstance(){
if (null==singleton) {
singleton = new Singleton();
}
return singleton;
}
}
测试类如下
package com.terisadeng.creator.singleton;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class TestSingleton {
final CyclicBarrier barrier=new CyclicBarrier(2);
static Singleton singleton1=null;
static Singleton singleton2=null;
public static void main(String[] args) throws InterruptedException {
ExecutorService threadPool=Executors.newFixedThreadPool(2);
TestSingleton testSingleton=new TestSingleton();
Thread1 t1=testSingleton.new Thread1();
Thread2 t2=testSingleton.new Thread2();
threadPool.execute(t1);
threadPool.execute(t2);
threadPool.shutdown();
Thread.sleep(1000);
System.out.println("singleton1:"+singleton1);
System.out.println("singleton2:"+singleton2);
}
private class Thread1 extends Thread {
@Override
public void run() {
try {
barrier.await();
singleton1=Singleton.getInstance();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}
}
private class Thread2 extends Thread {
@Override
public void run() {
try {
barrier.await();
singleton2=Singleton.getInstance();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}
}
}
测试结果,可以看到有概率产生不同的对象
下面是线程安全的单例模式
package com.terisadeng.creator.singleton;
public class Singleton {
//通过volatile的内存可见性和避免指令重排序,保证初始化实例对象时不会出现问题
private static volatile Singleton singleton=null;
//私有化构造函数,避免创建多个实例对象
private Singleton(){
}
public static Singleton getInstance(){
//通过双重验证和synchronized保证线程安全
if (null==singleton) {
synchronized (Singleton.class) {
if (null==singleton) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
多次测试没有出现产生不同对象的情况
4、建造者模式
建造者模式和工厂模式类似,只不过工厂模式实现的是创建单个类的模式,而建造者模式是通过将多个属性的组合来创建复合对。
首先是一个产品类,一般是一个比较复杂的对象
package com.terisadeng.creator.builder;
/**
* 产品类
* @author SN
*
*/
public class Product {
//汽车品牌
private String brand;
//汽车型号
private String type;
//汽车颜 {MOD}
private String color;
public void produce(){
System.out.println("制造汽车的品牌:"+brand+",型号:"+type+",颜 {MOD}:"+color);
}
public void setBrand(String brand) {
this.brand = brand;
}
public void setType(String type) {
this.type = type;
}
public void setColor(String color) {
this.color = color;
}
}
然后引入一个抽象的建造者,其具体的创建功能交给子类完成,这样便于扩展建造者的功能。
package com.terisadeng.creator.builder;
/**
* 抽象建造者
* @author SN
*
*/
public interface Builder {
//创建对象
public void produce(String brand,String type,String color);
//返回对象
public Product getProduct();
}
建造者用于实现抽象建造者,创建具体的对象并返回对象。
package com.terisadeng.creator.builder;
/**
* 建造者实现类
* @author SN
*
*/
public class FactBuilder implements Builder{
private Product product=null;
@Override
public void produce(String brand, String type, String color) {
product=new Product();
product.setBrand(brand);
product.setType(type);
product.setColor(color);
}
@Override
public Product getProduct() {
return product;
}
}
还有一个导演类用于调用建造者创建对象,导演类不直接与产品对象交互,用于封装创建对象的易变的部分。
package com.terisadeng.creator.builder;
/**
* 导演类
* @author SN
*
*/
public class Director {
private Builder builder=new FactBuilder();
public Product getProduct(String brand, String type, String color){
builder.produce(brand, type, color);
return builder.getProduct();
}
}
测试类
package com.terisadeng.creator.builder;
/**
* 测试建造者模式
* @author SN
*
*/
public class TestBuilder {
public static void main(String[] args) {
Director director=new Director();
Product p1=director.getProduct("BMW", "X7", "Black");
Product p2=director.getProduct("Audi", "A6", "White");
p1.produce();
p2.produce();
}
}
测试结果
5、
原型模式
原型模式就是以一个对象为原型,对其复制和克隆以产生一个和原对象类似的新对象。
说到复制对象就要提到浅复制和深复制。
浅复制:复制对象后,基本数据类型的变量会重新创建,引用类型的变量仍然指向原来的对象。
深复制:复制复制对象后,基本数据类型的变量和引用类型的变量都重新创建。
首先创建被复制的类即原型类,浅复制需要使用Object类的clone()方法进行复制,其是一个native方法。
package com.terisadeng.creator.prototype;
/**
* 原型类,调用Object类的clone()方法进行浅复制
* @author SN
*
*/
public class ProtoType implements Cloneable{
public Object copy() throws CloneNotSupportedException{
ProtoType protoType=(ProtoType) super.clone();
return protoType;
}
}
下面通过字节流来实现深复制
package com.terisadeng.creator.prototype;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
/**
* 原型类,调用Object类的clone()方法进行浅复制
* @author SN
*
*/
public class ProtoType implements Cloneable,Serializable{
private static final long serialVersionUID=1L;
private int i;
private Value value;
//浅复制
public Object copy() throws CloneNotSupportedException{
ProtoType protoType=(ProtoType) super.clone();
return protoType;
}
//深复制
public Object deepClone() throws IOException, ClassNotFoundException{
//写入当前对象的二进制流
ByteArrayOutputStream bos=new ByteArrayOutputStream();
ObjectOutputStream oos=new ObjectOutputStream(bos);
oos.writeObject(this);
//读取二进制流产生的新对象
ByteArrayInputStream bis=new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois=new ObjectInputStream(bis);
return ois.readObject();
}
public int getI() {
return i;
}
public void setI(int i) {
this.i = i;
}
public Value getValue() {
return value;
}
public void setValue(Value value) {
this.value = value;
}
public static long getSerialversionuid() {
return serialVersionUID;
}
}
class Value implements Serializable{
private static final long serialVersionUID=1L;
private String str;
public String getStr() {
return str;
}
public void setStr(String str) {
this.str = str;
}
public static long getSerialversionuid() {
return serialVersionUID;
}
}
测试类
package com.terisadeng.creator.prototype;
import java.io.IOException;
public class TestProtoType {
public static void main(String[] args){
ProtoType protoType=new ProtoType();
Value value=new Value();
value.setStr("test");
protoType.setValue(value);
ProtoType proto1=null;
//测试浅复制
System.out.println("##############测试浅复制##################");
try {
proto1 = (ProtoType) protoType.copy();
value.setStr("浅复制");
System.out.println("protoType.str:"+protoType.getValue().getStr());
System.out.println("proto1.str:"+proto1.getValue().getStr());
} catch (CloneNotSupportedException e1) {
e1.printStackTrace();
}
value.setStr("test");
ProtoType proto2=null;
//测试深复制
System.out.println("##############测试深复制##################");
try {
proto2=(ProtoType) protoType.deepClone();
value.setStr("深复制");
System.out.println("protoType.str:"+protoType.getValue().getStr());
System.out.println("proto2.str:"+proto2.getValue().getStr());
} catch (ClassNotFoundException | IOException e) {
e.printStackTrace();
}
}
}
测试结果,可以看到在浅复制后修改原型的值后,被复制对象的值也被修改了,说明浅复制的引用类型仍然指向原型类的引用类型的对象值,而深复制的引用类型值在原型对象修改之后并不会随着更改。