1. 程式人生 > 其它 >【設計模式】模板,單例,工廠,代理

【設計模式】模板,單例,工廠,代理

技術標籤:LeetCode設計模式javaspring

文章目錄


1.模板

在這裡插入圖片描述

package com.atguigu.test02.abstract_;
/*
 * 設計模式?解決問題的套路,程式碼結構。Java中常用的設計模式有23種。
 * 舉例:編寫一個類,包含一個方法,可以統計  你執行任意程式碼的執行時間
 * 步驟:(1)獲取開始時系統時間
 *       (2)執行xxxx
 *       (3)獲取結束時系統時間
 *       (4)計算時間差
 * 時間的單位:毫秒。提示:System.currentTimeMillis()
 */
public class TestTemplate { public static void main(String[] args) { MyCalTime my = new MyCalTime(); long time = my.getTime(); System.out.println("耗時:" + time + "毫秒"); } } abstract class CalTime{ //模板類 //可以計算任意一段程式碼的執行時間 //這裡加final的目的是不希望子類重寫,改寫我的演算法的結構 public final long
getTime(){ long start = System.currentTimeMillis(); //(1)獲取開始時系統時間 doWork(); //(2)執行xxxx long end = System.currentTimeMillis(); //(3)獲取結束時系統時間 return end - start; //(4)計算時間差 } protected abstract void doWork(); //protected的目的,希望只是子類中進行訪問和重寫 } class MyCalTime extends CalTime{ @Override
protected void doWork() { //重寫抽象方法 long sum = 0; for(int i=1; i<=100000; i++){ sum += i; } System.out.println("sum = " + sum); } }

在這裡插入圖片描述

2.單例

在這裡插入圖片描述

package com.atguigu.test17;
import org.junit.Test;
/*
 * 單例:某個類只能有唯一的一個例項物件。如何實現單例?
 * 
 * 1、餓/惡漢式: 不管我們使用者是否需要這個物件,它都上來先給你建立好這個唯一的物件。
 * (1)列舉型別
 * (2)形式二:class SingleClass
 * ①構造器私有化
 * ②用一個全域性的靜態的常量,來儲存這個唯一的例項物件
 * (3)形式三:class Single
 * ①構造器私有化
 * ②用一個私有的靜態的常量,來儲存這個唯一的例項物件
 * ③提供一個靜態方法,來返回這個常量物件
 * 
 * 2、懶漢式: 延遲建立物件。當使用者來或者這個物件,要用到物件時,我再建立。
 * (1)形式一:見下面,考慮執行緒安全問題和效能問題
 * (2)形式二:內部類形式
 * 
 */
public class Test17 {
	@Test
	public void test1(){
		SingleEnum s1 = SingleEnum.INSTANCE;
		SingleEnum s2 = SingleEnum.INSTANCE;
		System.out.println(s1 == s2); //true 
	}	
	@Test
	public void test2(){
//		SingleEnum.test();//此時我並沒有需要用到這個INSTANCE物件,但是它也創建出來SingleEnum物件,單例惡漢式
	}	
	@Test
	public void test3(){
		SingleClass s1 = SingleClass.INSTANCE;
		SingleClass s2 = SingleClass.INSTANCE;
		System.out.println(s1==s2); //true
	}	
	@Test
	public void test4(){
		Single s1 = Single.getInstance();
		Single s2 = Single.getInstance();
		System.out.println(s1 == s2);
	}	
	@Test
	public void test5(){
		LazyClass s1 = LazyClass.getInstance();
		LazyClass s2 = LazyClass.getInstance();
		System.out.println(s2 == s1);
	}	
	LazyClass s1;
	LazyClass s2;
	@Test
	public void test6(){
		//匿名的內部類,繼承Thread類。=後面是子類,然後多型
		Thread t1 = new Thread(){
			public void run(){
				s1 = LazyClass.getInstance();
			}
		};		
		Thread t2 = new Thread(){
			public void run(){
				s2 = LazyClass.getInstance();
			}
		};		
		t1.start();
		t2.start();		
		try {
			//這裡用join的目的是,為了兩個子執行緒都執行完,再執行主執行緒的System.out.println(s1);
			t1.join();
			t2.join();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}		
		System.out.println(s1);
		System.out.println(s2);
		System.out.println(s1 == s2);
	}	
}

//1111111111111111111111111111111111111111111111111111111111111111
enum SingleEnum{
	INSTANCE; //單例,列舉只有一個
//	public static void test(){
//		//..
//	}
}
class SingleClass{ //老版的列舉
	public static final SingleClass INSTANCE = new SingleClass();
	private SingleClass(){		
	}
}
class Single{
	private static final Single INSTANCE = new Single();
	private Single(){		
	}
	public static Single getInstance(){
		return INSTANCE;
	}
}

//111111111111111111111111111111111111111111111111111111111111111111
class LazyClass{
	private static LazyClass instance; //不加final,可以不用new
	private LazyClass(){		
	}	
	public static LazyClass getInstance(){
		if(instance == null){//提高效率,已建立物件就沒必要再進去,直接最後return instance;
			synchronized(LazyClass.class){//當前類的Class物件,靜態方法不能出現this
				if(instance == null){//安全判斷
					try {
						Thread.sleep(100);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					instance = new LazyClass();
				}
			}
		}
		return instance;
	}	
	//安全沒問題,但是認為不是最優的,效能差,因為後面沒必要再等鎖
/* public synchronized static LazyClass getInstance(){
		if(instance == null){
			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			instance = new LazyClass();
		}
		return instance;
	}*/	
	//有安全問題
/*	public static LazyClass getInstance(){
//		return new LazyClass();//錯誤的,每次都new新的物件
		if(instance == null){ //下次有的時候就不進來了
			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			instance = new LazyClass();
		}
		return instance;
	}*/
}

//11111111111111111111111111111111111111111111111111111
class Lazy{
	private Lazy(){		
	}	
	private static class Inner{
		public static final Lazy INSTANCE = new Lazy();//在內部類中,建立外部類的唯一物件
	}	
	public static Lazy getInstance(){ 
		return Inner.INSTANCE; //用到getInstance方法才會new Lazy(),也是懶漢式
	}
}

3.工廠

package com.atguigu.test12;
import org.junit.Test;
/* 
 * 工廠設計模式:為了解耦合,把物件的建立者new與物件的使用者分開。
 * 1、簡單工廠模式:優點:程式碼比較簡潔。缺點:如果增加新的產品型別(奧迪),需要修改工廠類。違反了面向物件的一個開發原則:對擴充套件開放,對修改關閉。
*/
public class TestFactory {
	@Test
	public void test03(){
		Car c = SimpleFactory2.getCar("賓士");
		c.run();
	}	
	@Test
	public void test02(){
		Car c = SimpleFactory.getCar(); //靜態
		c.run();//這裡也是執行寶馬的run(),但是從頭至尾沒有出現BMW類,解耦合
	}	
	@Test
	public void test01(){ 
		//沒有工廠,如下建立物件和使用物件是同一個人完成
		BMW b = new BMW();	//比如spring容器,new時要初始化很多配置資訊才建立一個物件
		//使用物件,呼叫方法
		b.run();
	}
}
//1111111111111111111111111111111111111111111111111111
interface Car{
	void run();
}
class BMW implements Car{
	@Override
	public void run() {
		System.out.println("寶馬讓你在車裡面哭");
	}	
}
class Benz implements Car{
	@Override
	public void run() {
		System.out.println("賓士讓你在車蓋上哭");
	}
}
class Audi implements Car{ //多了一類車,下面工廠類需要再改變
	@Override
	public void run() {
		System.out.println("奧迪讓你在...");
	}
}
//1111111111111111111111111111111111111111111111111
class SimpleFactory{ //工廠類
	public static Car getCar(){
		return new BMW();
	}
}
class SimpleFactory2{
	public static Car getCar(String type){
		if("寶馬".equals(type)){
			return new BMW();
		}else if("賓士".equals(type)){
			return new Benz();
		}
		return null;
	}
}
package com.atguigu.test12;
/*
 * 2、工廠方法模式
 * 出發點:(1)為了生產物件與使用物件分開 (2)如果增加新產品,就不需要修改原來的工廠類
 * 優點:遵循了增加新產品,不修改原來的類的原則。 缺點:類太多了,一種車一個工廠
 */
public class TestFactory2 {
	public static void main(String[] args) {
		BaoMaFactory bf = new BaoMaFactory();
		Che c = bf.getChe();
		c.run();
	}
}

//1111111111111111111111111111111111111111111
interface Che{
	void run();
}
class BaoMa implements Che{
	@Override
	public void run() {
		System.out.println("寶馬");
	}	
}
class BenChi implements Che{
	@Override
	public void run() {
		System.out.println("賓士");
	}
}
class AoDi implements Che{
	@Override
	public void run() {
		System.out.println("奧迪");
	}
}

//111111111111111111111111111111111111111111
interface GongChang{
	Che getChe();
}
class BaoMaFactory implements GongChang{ 
	@Override
	public Che getChe() {
		return new BaoMa();
	}	
}
class BenChiFactory implements  GongChang{
	@Override
	public Che getChe() {
		return new BenChi();
	}	
}  //只需要增加一個工廠類,專門生產奧迪車,不動上面程式碼

如下結合上面的簡單工廠和工廠方法:

package com.atguigu.test12;

public class TestFactory3 {
	public static void main(String[] args)throws Exception {
		Vehicle c = SimpleFactory3.getVehicle("com.atguigu.test12.QQ");
		c.run();   //qq車	
		Vehicle c2 = SimpleFactory3.getVehicle("com.atguigu.test12.Aoto");
		c2.run();  //奧拓
	}
}
interface Vehicle{
	void run();
}
class QQ implements Vehicle{
	@Override
	public void run() {
		System.out.println("qq車");
	}	
}
class Aoto implements  Vehicle{
	@Override
	public void run() {
		System.out.println("奧拓");
	}	
}

//111111111111111111111111111111111111111
class SimpleFactory3{
	public static Vehicle getVehicle(String className)throws Exception{
		Class clazz = Class.forName(className);
		return (Vehicle) clazz.newInstance(); //clazz.newInstance()返回Object強轉為Vehicle類
	}
}

4.代理

package com.atguigu.test13;
import org.junit.Test;
/*
 * 當前這個類做一些事情,不太方便,因為這個事情是多變,重複的,我可以交給代理
 * 例如:專案經理說,在測試環境即開發階段(不是生產環境,上線就是生產環境),測試一下每一個方法的執行時間,並且記錄方法執行的日誌。開發完了,專案經理說,把那些程式碼(就是"add方法開始執行"等)都去掉。
 * 
 *代理模式:(1)主題介面:要求【代理類DAOProxy】與【被代理類UserDAOImpl】實現同一個介面,例如:DAO介面。(2)被代理者。(3)代理者:必須持有被代理者的引用。
 *
 *上面是靜態代理模式 缺點:一個代理類只能替一個代理主題(介面)代理工作,多定義一個介面又要寫一個代理類。所以用動態代理模式。
 */
public class TestAgent {
	public static void main(String[] args) {
		GoodsDAOImpl dao = new GoodsDAOImpl(); //正式程式碼不是下面測試程式碼
		dao.add(); //新增商品  //沒有其他的
	}	
	@Test
	public void test02(){
		//new GoodsDAOImpl()被代理者物件, new DAOProxy(x)代理者物件
		DAO d = new DAOProxy(new GoodsDAOImpl());
		d.add();
	}	
	@Test
	public void test01(){
		DAO d = new DAOProxy(new UserDAOImpl());
		d.add();
	}
}

//111111111111111111111111111111111111111111111111111111
interface DAO{
	void add();
/*	void update();
	void delete();
	void select();*/
}
class UserDAOImpl implements DAO{ //被代理類
	@Override
	public void add() {
//		System.out.println("add方法開始執行"); //這些程式碼冗餘
//		long start = System.currentTimeMillis();
		System.out.println("新增使用者"); //這行的上下程式碼就是方法的日誌
//		long end = System.currentTimeMillis();
//		System.out.println("執行時間:" + (end-start));
//		System.out.println("add方法執行結束");
	}
	
}
class GoodsDAOImpl implements DAO{ //被代理類
	@Override
	public void add() {
//		System.out.println("add方法開始執行");
//		long start = System.currentTimeMillis();
		System.out.println("新增商品");
//		long end = System.currentTimeMillis();
//		System.out.println("執行時間:" + (end-start));
//		System.out.println("add方法執行結束");
	}	
}

//1111111111111111111111111111111111111111111111111111111111111111111111
class DAOProxy implements DAO{ //代理類
	private DAO dao;//持有被代理者的引用,因為核心業務邏輯仍然交給被代理者自己完成	
	public DAOProxy(DAO dao) { //傳入物件
		super();
		this.dao = dao;
	}
	public void add(){
		System.out.println("add方法開始執行");
		long start = System.currentTimeMillis();		
//		System.out.println("新增商品");//核心業務邏輯交給被代理者自己完成
		dao.add(); //呼叫
		long end = System.currentTimeMillis();
		System.out.println("執行時間:" + (end-start));
		System.out.println("add方法執行結束");
	}
}

在這裡插入圖片描述

package com.atguigu.test13;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import org.junit.Test;
/*
 * 動態代理模式:Spring框架中。優點:一個代理工作處理器,可以替多個代理主題(介面)代理工作,只有代理工作內容一樣就可以(比如都要代理做記錄時間)。
 * 需要:(1)編寫一個代理工作處理器的類,這個類必須實現一個介面InvocationHandler
 *      (2)用JDK中提供了一個Proxy類,來建立代理類的物件 (3)呼叫方法
 * new ProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
 * 如上方法引數一:被代理者的類載入器物件
 * 引數二:被代理者實現的介面們
 * 引數三:代理者替被代理者要完成的工作的處理器物件      
 */
public class TestProxy {
	@Test
	public void test01() {
		AImpl a = new AImpl();//被代理者
		ClassLoader loader = a.getClass().getClassLoader();
		Class<?>[] interfaces = a.getClass().getInterfaces();
		Handler h = new Handler(a);		
		A proxy = (A) Proxy.newProxyInstance(loader, interfaces, h);
		proxy.a(); // a方法開始執行,a方法被執行,執行時間:1,a方法執行結束
	}	
	@Test
	public void test02() {
		BImpl b = new BImpl();//被代理者		
		ClassLoader loader = b.getClass().getClassLoader();
		Class<?>[] interfaces = b.getClass().getInterfaces();
		Handler h = new Handler(b);		
		B proxy = (B) Proxy.newProxyInstance(loader, interfaces, h);
		proxy.b();
	}
}

//1111111111111111111111111111111111111111111111111111111111111111111111
class Handler implements InvocationHandler{
	private Object target;//被代理者物件	
	public Handler(Object target) { //構造器就是讓外面傳進來,上面定義的target=外面傳的target
		super();
		this.target = target;
	}
	/*
	 * 如下方法不是程式呼叫的,是一會執行代理類物件的方法時自動呼叫:
	 * 引數一:代理類的物件
	 * 引數二:被代理者要執行的方法
	 * 引數三:被代理者要執行的方法需要的實參列表
	 */
	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		System.out.println(method.getName() + "方法開始執行");
		long start = System.currentTimeMillis();	
			
//		method.invoke(obj, args);//obj是要求寫被代理者物件
		Object value = method.invoke(target, args);	//被代理方法有返回值	
		
		long end = System.currentTimeMillis();
		System.out.println("執行時間:" + (end-start));
		System.out.println(method.getName() + "方法執行結束");		
		return value; //必須要返回
	}	
}

//1111111111111111111111111111111111111111111111111111111111111111111
interface A{ //主題1
	void a();
}
class AImpl implements A{ //被代理者1
	@Override 
	public void a() {
		System.out.println("a方法被執行");
	}	
}
interface B{ //主題2
	void b();
}
class BImpl implements B{ //被代理者2
	@Override
	public void b() {
		System.out.println("b方法被執行");
	}	
}