天天看点

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();}}

继续阅读