常用設計模式(上)

結合原理與實例介紹以下設計模式:單例,原型,工廠方法,抽象工廠,建造者,代理五種設計模式。

1.單例模式

定義:一個類只有一個實例,且該類能自行創建這個實例的一種模式。
實際運用:Spring容器中所有bean默認是單例的java.lang.Runtime類是單例的。
適用場景:Web配置對象,各種連接池等。
注意事項:阻止對象clone, 注意線程安全問題,多重類加載器也會破壞單例,序列化。

public class Runtime {
    private static Runtime currentRuntime = new Runtime();
    public static Runtime getRuntime() {
        return currentRuntime;
    }
    private Runtime() {}
}

實例:單例分爲餓漢和懶漢模式。
餓漢:

public class SingleBean {
	private static final SingleBean INSTANCE = new SingleBean();
	private SingleBean() {};//私有化構造方法
	
	public static SingleBean getInstance() {
		return INSTANCE;
	}
}

懶漢:

public class SingleBean2 {
	
	private volatile static SingleBean2 INSTANCE = null;//需要有volatile修飾,保證多線程可見性
	
	private SingleBean2() {};

	//減少鎖粒度
	public static  SingleBean2 getInstance() {
		if(INSTANCE == null) {
			synchronized (SingleBean2.class) {
				if(INSTANCE == null) {
					INSTANCE = new SingleBean2();
				}
			}
		}
		return INSTANCE;
	}
}

實現單例還有一些其它方式:枚舉私有靜態內部類

	private static class Single{
		private static final SingleBean2 INSTANCE = new SingleBean2();
	}

2.原型模式

定義:用一個已經創建的實例作爲原型,通過複製該原型對象來創建一個和原型相同或相似的新對象。
實際運用:Java中Object類提供了clone方法(淺拷貝),java.lang.Cloneable方法

 protected native Object clone() throws CloneNotSupportedException;

適用場景:對象之間相同或相似,即只是個別的幾個屬性不同的時候。或者對象創建麻煩也clone方便。
淺拷貝:複製出來的對象的所有變量都含有與原來的對象相同的值,而所有的對其他對象的引用仍然指向原來的對象
深拷貝:複製出來的所有變量都含有與原來的對象相同的值,那些引用其他對象的變量將指向複製出來的新對象(將要clone的對象所引用的對象都複製了一遍)。

淺拷貝實例:clone出來student的teacher屬性依然指向原Student對象的teacher屬性地址。

public class Student implements Cloneable{
	
	private Integer age;
	private String name;
	private int stuNo;
	private Teacher teacher;
	
	@Override
	protected Student clone() {
		try {
			Student student = (Student) super.clone();
			return student;
		} catch (CloneNotSupportedException e) {
			e.printStackTrace();
		}
		return null;
	}
}

深拷貝實例:Teacher需要實現Serializable接口

public class Student2 implements Serializable{
	private static final long serialVersionUID = 1L;
	private Integer age;
	private String name;
	private int stuNo;
	private Teacher teacher;
	
	public Student2 clone() {//必須實現Serializable接口
		 try {
			 ByteArrayOutputStream bo = new ByteArrayOutputStream();
		     ObjectOutputStream oo = new ObjectOutputStream(bo);
		     oo.writeObject(this);
		     
		     ByteArrayInputStream bi = new ByteArrayInputStream(bo.toByteArray());
		     ObjectInputStream oi = new ObjectInputStream(bi);
		     Student2 student2 = (Student2) oi.readObject();
		     return student2;
		} catch (Exception e) {
			e.printStackTrace();
		}
		 return null;
	}
}

3. 工廠方法和抽象工廠模式

工廠方法:定義一個創建產品對象的工廠接口,將產品對象的實際創建工作推遲到具體子工廠類當中(當創建的產品就一個類別時,可退化爲簡單工廠模式→Spring BeanFactory)。

抽象工廠模式:爲訪問類提供一個創建一組相關或相互依賴對象的接口,且訪問類無須指定所要產品的具體類就能得到同族的不同等級的產品的模式結構。
看了上面的概率是不是覺得有點抽象

工廠方法實例
有汽車工廠生產汽車,汽車有不同的品牌,對應品牌的工廠才能生產對應品牌的汽車。

// 汽車工廠
public interface CarFactory {
	Car createCar();
}
public abstract class Car {
	abstract void show();
}

寶馬工廠:

public class BMWFactory implements CarFactory{
	@Override
	public Car createCar() {
		return new BMWCar();
	}
}
public class BMWCar extends Car{
	@Override
	void show() {
		System.out.println("BMWCar show...");
	}
}

本田工廠:

public class HFactory implements CarFactory{
	@Override
	public Car createCar() {
		return new HCar();
	}
}
public class HCar extends Car{
	@Override
	void show() {
		System.out.println("HCar show...");
	}
}

具體測試:

public class Test {
	public static void main(String[] args) {
		//客戶不需要知道具體的產品名,只需要知道對應的工廠即可
		CarFactory bmwFactory = new BMWFactory();
		bmwFactory.createCar().show();
		CarFactory hFactory = new HFactory();
		hFactory.createCar().show();
	}
}

抽象工廠實例
工廠能生產一類產品,比如電腦,手機,平板電腦,依然對應不同的品牌(小米,華爲,蘋果),對應品牌的工廠能生成對應的電腦,手機和平板電腦。

abstract class Factory {
	public abstract AbstractProduct createComputer();
	public abstract AbstractProduct createPhone();
}
public abstract class AbstractProduct {
	public abstract void show();
}

小米工廠:

public class XiaoMiFactory extends Factory{
	private static Factory XiaoMiFactory = new XiaoMiFactory();
	public static Factory getFactory() {
		return XiaoMiFactory;
	}
	@Override
	public AbstractProduct createComputer() {
		return new XiaoMiComputer();
	}
	@Override
	public AbstractProduct createPhone() {
		return new XiaoMiPhone();
	}
}

華爲工廠:

public class HuaWeiFactory extends Factory{
	private static Factory huaweiFactory = new HuaWeiFactory();
	public static Factory getFactory() {
		return huaweiFactory;
	}
	@Override
	public AbstractProduct createComputer() {
		return new HuaWeiComputer();
	}
	@Override
	public AbstractProduct createPhone() {
		return new HuaWeiPhone();
	}
}

通過實例對比:
工廠方法是:一個品牌的工廠生產一個品牌的產品
抽象工廠是:一個品牌的工廠生產一類品牌的產品

4.建造者模式

定義:將一個複雜對象的構造與它的表示分離,使同樣的構建過程可以創建不同的表示。
實際運用:StringBuilder , Spring的RestTemplate,ElasticSearch的Java sdk。
在我們實際運用中,我們就是導演,所以下面示例沒有這個角色

實例:根據指定的配置,組裝電腦

public  class Computer {

	private final String cpu;
	private final String memory;
	private final String disk;
	private final String power;

	public String getCpu() {
		return cpu;
	}

	public String getMemory() {
		return memory;
	}

	public String getDisk() {
		return disk;
	}

	public String getPower() {
		return power;
	}

	public static class Builder {
		private String cpu;
		private String memory;
		private String disk;
		private String power="超大電池";//設置屬性默認值

		public Builder() {
		};

		public Builder cpu(String cpu) {
			this.cpu = cpu;
			return this;
		}

		public Builder memory(String memory) {
			//可以加入更復雜的處理邏輯
			this.memory = memory;
			return this;
		}

		public Builder disk(String disk) {
			this.disk = disk;
			return this;
		}

		public Builder power(String power) {
			this.power = power;
			return this;
		}
		public Computer build() {//檢驗必需屬性
			if(cpu == null) {
				throw new IllegalStateException("cpu Can't be empty");
			}
			return new Computer(this);
		}
		public Builder (Computer computer) {
			this.cpu = computer.cpu;
			this.disk = computer.disk;
			this.memory = computer.memory;
			this.power = computer.power;
		}
	}
	private Computer(Builder b) {
		cpu = b.cpu;
		memory = b.memory;
		disk = b.disk;
		power = b.power;
	}
	
	public Builder newBuilder() {
		return new Builder(this);
	}

	@Override
	public String toString() {
		return "Computer [cpu=" + cpu + ", memory=" + memory + ", disk=" + disk + ", power=" + power + "]";
	}

測試:

public class Test {
	public static void main(String[] args) {
		Computer computer = new Computer.Builder().cpu("i7").disk("500G").power("超大電源").memory("500G固態").build();
		//修改屬性
		Computer newComputer = computer.newBuilder().cpu("i9").build();
		System.out.println(computer);
		System.out.println(newComputer);
	}
}

5.代理模式

代理模式分爲
jdk代理:實現InvocationHandler接口,加上反射機制生成代理接口的匿名實現類(基於接口生成代理類)。
cglib代理:利用ASM開源包,將代理對象類的class文件加載進來,通過修改其字節碼生成子類來處理(目標類不能被final修飾)。
實際應用:很多框架底層都採用了代理,比如Spring aop,mybatis等

實例:定義一個代理工廠類,實現兩種代理方式。模擬dao方法前後建立,關閉數據庫連接。

public class MapperFactory {

	// jdk代理
	public static UserDao getMapper(Class<UserDao> class1) {
		try {
			UserDao userDao = (UserDao) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(),
					new Class[] { UserDao.class }, new UserDaoProxy());
			return userDao;
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	// cglib代理
	public static StudentDao getMapper(StudentDao target) {
		StudentDao studentDao = (StudentDao) new StudentDaoProxy().getInstance(target);
		return studentDao;
	}

}

jdk代理:

public interface UserDao {
	User   getUserById(String id); 
}

public class UserDaoProxy implements InvocationHandler {
	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		if ("getUserById".equals(method.getName())) {
			System.out.println("開啓數據庫連接");
			User user = DataBase.getUser(args[0].toString());
			System.out.println("查詢完畢,關閉連接..");

			return user;
		}
		return null;
	}

}

public class User {
	
	private String id;
	private String name;
	public String getId() {
		return id;
	}
	public void setId(String id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public User() {
		super();
	}
	public User(String id, String name) {
		super();
		this.id = id;
		this.name = name;
	}
	@Override
	public String toString() {
		return "User [id=" + id + ", name=" + name + "]";
	}
}

/**
 * @Description:模擬從數據庫查詢用戶
 */
public class DataBase {
	private static Map<String, User> users = new ConcurrentHashMap<String, User>();
	static{
		init();
	}
	public static User getUser(String id) {
		User user = users.get(id);
		return user;
	}
	
	private static void init() {
		for(int i=0;i<15;i++) {
			users.put(String.valueOf(i), new User(String.valueOf(i), "test"+i));
		}
	}
}

cglib代理:

public class StudentDao {

	public User getStudentById(String id) {
		System.out.println("我只負責打醬油..");
		return null;
	}
}

public class StudentDaoProxy implements MethodInterceptor {

	// 委託對象,運行時定類型
	private Object target;

	public Object getInstance(Object target) {
		this.target = target;
		Enhancer enhancer = new Enhancer();
		enhancer.setSuperclass(this.target.getClass());
		// 回調方法
		enhancer.setCallback(this);
		// 創建代理對象
		return enhancer.create();
	}

	@Override
	public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
		System.out.println("開啓連接");
		proxy.invokeSuper(obj, args);
		User user = DataBase.getUser(args[0].toString());
		System.out.println("關閉連接");
		return user;
	}
}

測試類:

public class Test {
	public static void main(String[] args) {
		// jdk動態代理
		UserDao userDao = MapperFactory.getMapper(UserDao.class);
		User user = userDao.getUserById("2");
		System.out.println(user);

		// cglib代理
		StudentDao studentDao = MapperFactory.getMapper(new StudentDao());
		User s = studentDao.getStudentById("5");
		System.out.println(s);
	}
}

參考地址:http://c.biancheng.net/view/1395.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章