天天看點

java設計模式(1)-------單例,工廠,值對象,裝飾模式

                                                                                                                                  java設計模式(1)

      先簡單的介紹下設計模式吧:是一種解決問題的一種行之有效的思想;用于解決特定環境下、重複出現的特定問題的解決方案。

      那為什麼我們需要學習設計模式呢?

        1、設計模式都是一些相對優秀的解決方案,很多問題都是典型的、有代表性的問題,學習設計模式,我們就不用自己從頭來解決這些問題,相當于在巨人的肩膀上,複用這些方案即可,站的高看到遠,就是要站在巨人的肩膀上把他們踩下去,嘿嘿。

        2、設計模式已經成為專業人士的常用詞彙,不懂不利于交流,能讓你變得很牛逼哦。

        3、能讓你設計的系統更加專業,讓系統有更好的架構,能讓你變成大牛哦。

下面介紹幾種設計模式:

一、單例

         如果要求一個類的記憶體中隻有一個類的話可以采用單例解決。

        1、思路:

              ①、如果其他程式能夠随意用new建立該類對象,那麼就無法控制個數。是以,不讓其他程式用new建立該類的對象。

              ②、既然不讓其他程式new該類對象,那麼該類在自己内部就要建立一個對象,否則該類就永遠無法建立對象了。

              ③、該類将建立的對象對外(整個系統)提供,讓其他程式擷取并使用。

        2、步驟:

              ①、将該類中的構造函數私有化。

              ②、在本類中建立一個本類對象。

              ③、定義一個方法,傳回值類型是本類類型。讓其他程式通過該方法就可以擷取到該類對象。

3、代碼:

              ①、餓漢式:

class Single{
	private static final Single s = new Single();
	private Single(){
            }
	public static Single getInstance(){
	    return s;
	}
}
           

              ②、懶漢式:(單例的延遲加載方式)

class Single2 {
	private static Single2 s = null;
	private Single2(){
            }
	public static Single2 getInstance() {  //(PS:如果要考慮線程安全的話,應該這樣寫:public static synchronized Single2 getInstance(){)
		if(s==null)
			s=new Single2();
		return s;
	}
}
           

        4、單例變形(多例)

               ①、緩存在單例中的使用(“單例+緩存”技術)

                      要求:緩存在程式設計中使用很頻繁,有着非常重要的作用,它能夠幫助程式實作以空間換取時間,通常被設計成整個應用程式所共享的一個空間,現要求實作一個用緩存存放單例對象的類。說明:該緩存中可以存放多個該類對象,每個對象以一個key值辨別,key值相同時所通路的是同一個單例對象

                     代碼:

import java.util.HashMap;
import java.util.Map;
public class A {
	//定義一個緩存(集合),用來存放資料的容器
	private static Map<String,A> map = new HashMap<String,A>();
	public static A getInstance(String key){
		A a = map.get(key);
		//判斷a是否存在,不存在則是null
		if(a==null){
			a = new A();//建立一個對象
			map.put(key, a);//把新對象放入緩存
		}
		return a;
	}
}
           

                ②、單例變形——多例模式(“單例+緩存+控制執行個體個數”技術)

                        要求:把上面緩存的單例實作,做成一個能夠控制對象個數的共享空間,供整個應用程式使用。在緩存中維護指定個數的對象,每個對象的key值由該類内部指定,有外部請求時直接傳回其中一個對象出去。說明:相當于維護一個指定數量的對象池,當請求個數超過控制的總數時,開始循環重複使用 。

           代碼:

import java.util.HashMap;
import java.util.Map;
public class Multiple {
	private static Map<Integer,Multiple> map = new HashMap<Integer,Multiple>();
	private static int num=1;
	private static int count=3;//控制執行個體的個數:3
	public static Multiple getInstance(){
		Multiple m = map.get(num);
		if(m==null){
			m = new Multiple();
			map.put(num, m);
		}
		num++;
		//如果num超過所控制的個數,則重新設為1,以進行循環重複使用緩存中的對象
		if(num>count){
			num=1;
		}
		return m;
	}
	public static void main(String[] args) {
		//測試案例
		Multiple m1 = Multiple.getInstance();
		System.out.println("m1:::"+m1);
		Multiple m2 = Multiple.getInstance();
		System.out.println("m2:::"+m2);
		Multiple m3 = Multiple.getInstance();
		System.out.println("m3:::"+m3);
		Multiple m4 = Multiple.getInstance();
		System.out.println("m4:::"+m4);
		Multiple m5 = Multiple.getInstance();
		System.out.println("m5:::"+m5);
	}
}
           

二、工廠

        1、我可以在java程式開發的時候講究面向接口程式設計,需要把隐藏具體的實作類的時候可以采用工程設計模式。

        2、工廠類的命名規範:***Factory;單例工廠方法的命名規範:getInstance()。

        3、工廠的本質是“選擇實作”

        4、工廠、接口和實作類之間的技術邊界:工廠隻負責選擇實作,實作類才做真正的實作,而接口是限定到底實作什麼東西和傳回什麼東西,三者之間分工明确、各負其責。

        5、通過代碼來詳細的介紹:

               ①、接口Api:

//接口Api
public interface Api {
	public abstract String t1();
}
           

               ②、兩個實作類Imp1和Imp2:(都是實作了接口Api)

class Imp1:

//實作類Impl
public class Impl implements Api {
	@Override
	public String t1() {
		return "11111111111111"; //這裡是隻是測試資料而已
	}
}
           

class Imp2:

//實作類Imp2
public class Impl2 implements Api {
	@Override
	public String t1() {
		return "222222";
	}
}
           

             ③、工廠DepFactory :

//工廠DepFactory 
public class DepFactory {
	public static Api createApi(){
		return new Impl2();//通過配置檔案+類反射,讓我們的程式依賴字元串
	}
}
           

             ④、測試類(使用類):

public class Client {
	public static void main(String[] args) {
		Api obj = DepFactory.createApi();//new Impl();
		String str = obj.t1();
		System.out.println(str);
	}
}
           

三、值對象

         1、在java開發過程中,需要來回交換大量的資料的時候可以采用值對象設計模式。

         2、值對象的本質是“封裝資料”

         3、基本的編寫步驟:

                第1步:寫一個類,實作可序列化(如果以後資料是往資料庫裡存的,那麼可以不序列化,節省資源)

                第2步:私有化所有屬性,保持一個預設構造方法(public無參)

                第3步:為每個屬性提供get()、set()方法(如果是boolean型變量,最好把get改成is)

                第4步:推薦覆寫實作equals()、hashCode()和toString()方法

          4、代碼實作:

import java.io.Serializable;
//值對象
public class UserModel implements Serializable { //實作序列化
        //私有化所有屬性
	private String id,name,address;
	private boolean man;
	public UserModel(String name){
		this.name = name;
	}
	public UserModel(){
	}
	//給所有屬性提供get(),set()方法
	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 String getAddress() {
		return address;
	}
	public void setAddress(String address) {
		this.address = address;
	}
	public boolean isMan() {
		return man;
	}
	public void setMan(boolean man) {
		this.man = man;
	}
	
	//hashCode和equals一般隻用“主鍵”來生成
 	//覆寫hashCode(),equals(),toString()方法
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((id == null) ? 0 : id.hashCode());
		return result;
	}
	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		UserModel other = (UserModel) obj;
		if (id == null) {
			if (other.id != null)
				return false;
		} else if (!id.equals(other.id))
			return false;
		return true;
	}
	@Override
	public String toString() {
		return "UserModel [id=" + id + ", name=" + name + ", address="
				+ address + ", man=" + man + "]";
	}
}
           

四、裝飾模式

         1、在不對原有對象類進行修改的基礎上,需要給一個或多個已有的類對象提供增強額外的功能的時候可以采用裝飾設計模式。

         2、引例:

                 要求:寫一個MyBufferedReader類,使它能夠對字元流(如FileReader、InputStreamReader和PipedReader等)進行功能增強:

                              (1) 提供帶緩沖的myRead()方法,對原有的read()方法進行增速;

                              (2)提供一個能夠每次讀取一行字元的myReadLine()方法。

                 代碼:

import java.io.FileReader;
import java.io.IOException;
public class MyBufferedReader{
	private FileReader r;//封裝(這裡可以根據需要改成InputStreamReader和PipedReader等)
	private char[] buf = new char[1024];
	private int count=0;//記錄目前緩沖區中字元的個數
	private int pos=0;//數組元素的下标,目前所讀的位置
	//構造函數
	public MyBufferedReader(FileReader r){
		this.r = r;
	}
	//讀單個字元函數
	public int myRead() throws IOException{
		if(count==0){
			count = r.read(buf);
			pos=0;
		}
		if(count==-1){
			return -1;//表示已經讀完了最後一個元素
		}
		char ch = buf[pos];
		pos++;
		count--;
		return ch;
	}
	//讀一行字元函數
	public String myReadLine() throws IOException{
		StringBuilder sb = new StringBuilder();
		int ch=0;
		while((ch=myRead())!=-1){
			if(ch=='\r'){
				continue;
			}
			if(ch=='\n'){
				return sb.toString();//遇到回車就傳回這行字元串
			}
			sb.append((char)ch);
		}
		//這裡是保證最後一行沒有回車的字元串的輸出
		if(sb.length()!=0)
		   return sb.toString();
		return null;
	}
	//抛異常函數
	public void close() throws IOException{
		r.close();
	}
}
           

把上面代碼結合在别的類使用:

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
public class TestMyBufferedReader {
    	//main函數
	public static void main(String[] args) {
		try {
 			//隻讀一個字元
			readTest();//原始測試
			myReadTest();//我們自己寫的加強測試
			//讀一行
			readLineTest();//原始測試
			myReadLineTest();//我們自己寫的加強測試
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	//-------------------------------------------//
           
<span style="white-space:pre">	</span>//API原始方法
	private static void readTest() throws IOException {
		FileReader r = new FileReader("a.txt");
		BufferedReader br = new BufferedReader(r);
		int ch=0;
		while((ch=br.read())!=-1){
			System.out.print((char)ch);
		}
		br.close();
		r.close();
	}
           
<span style="white-space:pre">	</span>//我們自己寫的加強版
	private static void myReadTest() throws IOException {
		FileReader r = new FileReader("a.txt");
		MyBufferedReader br = new MyBufferedReader(r);
		int ch=0;
		while((ch=br.myRead())!=-1){
			System.out.print((char)ch);
		}
		br.close();
		r.close();
	}
	//-------------------------------------------//
	<pre name="code" class="java"><span style="white-space:pre">	</span>//API原始方法
           

private static void readLineTest() throws IOException {FileReader r = new FileReader("a.txt");BufferedReader br = new BufferedReader(r);String line=null;while((line=br.readLine())!=null){System.out.println(line);}br.close();r.close();}

<span style="white-space:pre">	</span><pre name="code" class="java"><span style="white-space:pre">	</span>//我們自己寫的加強版
           

private static void myReadLineTest() throws IOException {FileReader r = new FileReader("a.txt");MyBufferedReader br = new MyBufferedReader(r);String line=null;while((line=br.myReadLine())!=null){System.out.println(line);}br.close();r.close();}}

繼續閱讀