Java常用设计模式

设计模式分为三种类型,共23种:

创建型模式:单例模式、抽象工厂模式、建造者模式、工厂模式、原型模式。

结构型模式:适配器模式、桥接模式、装饰模式、组合模式、外观模式、享元模式、代理模式。

行为型模式:模版方法模式、命令模式、迭代器模式、观察者模式、中介者模式、备忘录模式、解释器模式、状态模式、策略模式、职责链模式(责任链模式)、访问者模式。

单例模式

每个类只能创建一个实例对象

Java 单例模式主要作用是保证在Java应用程序中,一个类Class只有一个实例存在。 使用Singleton的好处还在于可以节省内存,因为它限制了实例的个数,有利于Java垃圾回收(garbage collection)。

好处:

第一、控制资源的使用,通过线程同步来控制资源的并发访问;

第二、控制实例产生的数量,达到节约资源的目的。

第三、作为通信媒介使用,也就是数据共享,它可以在不建立直接关联的条件下,让多个不相关的两个线程或者进程之间实现通信。

package javatest;


//设计模式之单例模式
public class Singleton {
	
	String name = null;
	
	private Singleton(){
		
	};
	
	private static volatile Singleton instance = null;
	
	//懒汉式写法(线程安全, 不带synchronized就是线程不安全
	public static Singleton getInstance(){
		if (instance == null) {
			synchronized (Singleton.class) {
				//synchronized 关键字,代表这个方法加锁,相当于不管哪一个线程A每次运行到这个方法时,都要检查有没有其它正在用这个方法的线程B(或者C D等,
				//要不然在多线程情况下可能2个线程同时创建对象
				if (instance == null) {
					instance = new Singleton();
				}
			}
		}
		return instance;
	}
	
	/*
	 * 饿汉式写法, 类加载时直接将对象创建好, 用方法直接返回, 不实时创建
	 public class Singleton {  
   		private static Singleton instance = new Singleton();  
   		private Singleton (){}  
   		public static Singleton getInstance() {  
   		return instance;  
   		}  
	}
	 */
	
	/*
	 *静态内部类写法 
	 public class Singleton {  
   		private static class SingletonHolder {  
   		private static final Singleton INSTANCE = new Singleton();  
   		}  
   		private Singleton (){}  
   		public static final Singleton getInstance() {  
   		return SingletonHolder.INSTANCE;  
   	}  
}
	 */
	
	/*
	 * 枚举写法
	 public enum Singleton {  
   		INSTANCE;  
   		public void whateverMethod() {  
   		}  
	}
	 */
	
	public String getName(){
		return name;
	}
	
	public void setName(String name){
		this.name = name;
	}
	
	public void getInfo(){
		System.out.println("name is: " + name);
	}

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Singleton s1 = Singleton.getInstance();
		s1.setName("kevin");
		
		Singleton s2 = Singleton.getInstance();
		s2.setName("shelley");
		
		s1.getInfo();
		s2.getInfo();
		
		if (s1.equals(s2)) {
			System.out.println("是一个实例");
		}else {
			System.out.println("不是一个实例");
		}
	}
}

观察者模式

观察者模式定义了一个一对多的依赖关系,让一个或多个观察者对象监听一个主题对象。这样一来,当被观察者状态发生改变时,需要通知相应的观察者,使这些观察者对象能够自动更新。例如:GUI中的事件处理机制采用的就是观察者模式。
流程:

  1. 创建被观察者(自己的属性, 被观察者注册删除通知方法, 被观察者列表)
  2. 创建多个观察者(自己的属性, 被通知后需要做的事情)
  3. 将观察者注册并加入被观察者的通知列表
  4. 被观察者属性变动时,触发通知方法通知所有观察者
package javatest;

import java.util.ArrayList;

//设计模式-观察者模式

interface Observable{
	public void registerObserve(Observer observer);
	public void removeObserve(Observer observer);
	public void notifyObserver();
	
}

class Beenobserve implements Observable{
	
	private ArrayList<Observer> olist = new ArrayList<>();
	private float price;
	
	public Beenobserve(float price){
		this.price = price;
	}
	
	public float getPrice(){
		return price;
	}
	
	public void setPrice(float price){
		this.price = price;
		notifyObserver();
	}
	
	@Override
	public void registerObserve(Observer observer){
		olist.add(observer);
	}
	
	@Override
	public void removeObserve(Observer observer){
		olist.remove(observer);
	}
	
	@Override
	public void notifyObserver(){
		
		for(Observer observer:olist){
			observer.update(price);
		}
	}
	
}

interface iObserver {
	public void update(float price);
}
public class Observer implements iObserver{
	
	private String name;
	public Observer(String name){
		this.name = name;
	}
	
	@Override
	public void update(float price){
		System.out.println(name + "关注的杯子的价格已更新为: " + price);
	}

	
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		
		Beenobserve beb = new Beenobserve(3000);
		Observer o1 = new Observer("kevin");
		Observer o2 = new Observer("shelley");
		
		beb.registerObserve(o1);
		beb.registerObserve(o2);
		System.out.println("第一次修改价格");
		beb.setPrice(3200);
		System.out.println("第二次修改价格");
		beb.setPrice(2800);
		beb.removeObserve(o2);
		System.out.println("第三次修改价格");
		beb.setPrice(2000);
	}
	
}

工厂模式

优点:

将创建实例的工作与使用实例的工作分开,使用者不必关心类对象如何创建,明确了职责。

把初始化实例时的工作放到工厂里进行,使代码更容易维护。 更符合面向对象的原则,面向接口编程,而不是面向实现编程。

缺点:

由于工厂类集中了所有产品创建逻辑,一旦不能正常工作,整个系统都要受到影响。

要新增产品类的时候,就要修改工厂类的代码,违反了开放封闭原则(对扩展的开放,对修改的关闭)。(可以用反射机制解决)

简单工厂模式由于使用了静态工厂方法,静态方法不能被继承和重写,会造成工厂角色无法形成基于继承的等级结构。

package javatest;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

//设计模式之-工厂模式
interface Sender{
	public void Send();
}

class Mailsender implements Sender{
	@Override
	public void Send(){
		System.out.println("this is mail sender");
	}
}

class Smssender implements Sender{
	@Override
	public void Send(){
		System.out.println("this is sms sender");
	}
}

class Wechatsender implements Sender{
	@Override
	public void Send(){
		System.out.println("this is wechat sender");
	}
}

public class Factory {
	
	//普通工厂模式
	public Sender produce(String type){
		if("mail".equals(type)){
			return new Mailsender();
		}else if("sms".equals(type)){
			return new Smssender();
		}else if("wechat".equals(type)){
			return new Wechatsender();
		}else{
			System.out.println("类型不正确");
			return null;
		}
		
	}
	
	//多工厂方法模式
	public Sender produceMail(){
		return new Mailsender();
	}
	
	public Sender produceSms(){
		return new Smssender();
	}
	
	//通过反射机制, 定义通用工厂, 接收参数决定生产的产品
	public void produceOjbect(String s) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, SecurityException, IllegalArgumentException, InvocationTargetException{
		String className = s;     //需要运行时加载的类
		Class clz3 = Class.forName(className);	  //使用forName静态方法加载类
		Object obj = clz3.newInstance();	
		Method nameMethod1 = clz3.getMethod("Send");  //第二个值是该方法型参的类型,有多个型参用逗号隔开;方法没有型参,忽略第二个参数
		//调用此方法
		nameMethod1.invoke(obj);
	}


	public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, SecurityException, IllegalArgumentException, InvocationTargetException {
		// TODO Auto-generated method stub
		
		//普通工厂模式
		Factory f = new Factory();
		Sender s = f.produce("sms");
		s.Send();
		
		//多工厂模式
		Factory f2 = new Factory();
		Sender s2 = f2.produceMail();
		s2.Send();
		
		//反射模式通用工厂
		Factory f3 = new Factory();
		f3.produceOjbect("javatest.Wechatsender");	

	}

}

代理模式

基本概念:为其他对象提供一种代理以控制对这个对象的访问。也可以说,在出发点到目的地之间有一道中间层,意为代理。

为什么要使用

授权机制不同级别的用户对同一对象拥有不同的访问权利,如在论坛系统中,就使用Proxy进行授权机制控制,访问论坛有两种人:注册用户和游客(未注册用户),论坛就通过类似ForumProxy这样的代理来控制这两种用户对论坛的访问权限。

某个客户端不能直接操作到某个对象,但又必须和那个对象有所互动。

举例两个具体情况:

如果那个对象是一个是很大的图片,需要花费很长时间才能显示出来,那么当这个图片包含在文档中时,使用编辑器或浏览器打开这个文档,打开文档必须很迅速,不能等待大图片处理完成,这时需要做个图片Proxy来代替真正的图片。
如果那个对象在Internet的某个远端服务器上,直接操作这个对象因为网络速度原因可能比较慢,那我们可以先用Proxy来代替那个对象。
总之原则是,对于开销很大的对象,只有在使用它时才创建,这个原则可以为我们节省很多宝贵的Java内存。所以,有些人认为Java耗费资源内存,我以为这和程序编制思路也有一定的关系。

如何使用

以论坛系统为例,访问论坛系统的用户有多种类型:注册普通用户、论坛管理者、系统管理者、游客。注册普通用户才能发言,论坛管理者可以管理他被授权的论坛,系统管理者可以管理所有事务等,这些权限划分和管理是使用Proxy完成的。
在Forum中陈列了有关论坛操作的主要行为,如论坛名称,论坛描述的获取和修改,帖子发表删除编辑等,在ForumPermissions中定义了各种级别权限的用户:

public class ForumPermissions implements Cacheable {
    /**
    * Permission to read object.
    */
    public static final int READ = 0;

    /**
    * Permission to administer the entire sytem.
    */
    public static final int SYSTEM_ADMIN = 1;

    /**
    * Permission to administer a particular forum.
    */
    public static final int FORUM_ADMIN = 2;

    /**
    * Permission to administer a particular user.
    */
    public static final int USER_ADMIN = 3;
  
    public boolean isSystemOrForumAdmin() {
        return (values[FORUM_ADMIN] || values[SYSTEM_ADMIN]);
    }

//相关操作代码
}

因此,Forum中各种操作权限是和ForumPermissions定义的用户级别有关系的,作为接口Forum的实现:ForumProxy正是将这种对应关系联系起来。比如,修改Forum的名称,只有论坛管理者或系统管理者可以修改,代码如下:

public class ForumProxy implements Forum {
    private ForumPermissions permissions;
    private Forum forum;
    this.authorization = authorization;

    public ForumProxy(Forum forum, Authorization authorization,ForumPermissions permissions){
        this.forum = forum;
        this.authorization = authorization;
        this.permissions = permissions;
    }
    .....
    public void setName(String name) throws UnauthorizedException,
        ForumAlreadyExistsException{
        //只有是系统或论坛管理者才可以修改名称
      if (permissions.isSystemOrForumAdmin()) {
        forum.setName(name);  //代理
      }
    else {
    throw new UnauthorizedException();
    }
    }
    ...

}

而DbForum才是接口Forum的真正实现,以修改论坛名称为例:

public class DbForum implements Forum, Cacheable {
    ...
    public void setName(String name) throws ForumAlreadyExistsException {
  ....
        this.name = name;
       //这里真正将新名称保存到数据库中
       saveToDb();
  ....
    }
    ...
}

凡是涉及到对论坛名称修改这一事件,其他程序都首先得和ForumProxy打交道,由ForumProxy决定是否有权限做某一样事情,ForumProxy是个名副其实的"网关",“安全代理系统”。

装饰者模式

Decorator模式有以下的优缺点:

比静态继承更灵活与对象的静态继承相比,Decorator模式提供了更加灵活的向对象添加职责的方式,可以使用添加和分离的方法,用装饰在运行时刻增加和删除职责。使用继承机制增加职责需要创建一个新的子类,如果需要为原来所有的子类都添加功能的话,每个子类都需要重写,增加系统的复杂度,此外可以为一个特定的Component类提供多个Decorator,这种混合匹配是适用继承很难做到的。

避免在层次结构高层的类有太多的特征,Decorator模式提供了一种“即用即付”的方法来添加职责,他并不试图在一个复杂的可订制的类中支持所有可预见的特征,相反可以定义一个简单的类,并且用Decorator类给他逐渐的添加功能,从简单的部件组合出复杂的功能。

Decorator 与它的Component不一样,Decorator是一个透明的包装,如果我们从对象标识的观点出发,一个被装饰了的组件与这个组件是有差别的,因此使用装饰时不应该以来对象标识。

产生许多小对象,采用Decorator模式进行系统设计往往会产生许多看上去类似的小对象,这些对象仅仅在他们相互连接的方式上有所不同。

流程:

  1. 创建接口类
  2. 创建具体需要被装饰的实体类, 实现接口
  3. 创建装饰类, 实现同一个接口
  4. 分别创建多个具体的装饰类, 继承于装饰类
package javatest;

//设计模式-装饰者模式
interface Daban{
	public void chuanyi();
}

class Man implements Daban{
	
	private String name;

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}
	
	public Man(String name){
		this.name = name;
	}
	
	@Override
	public void chuanyi(){
		System.out.println("穿衣打扮后的" + name);
	}
}

class DecoratorClient implements Daban{
	private Daban mDaban;
	public void decoratorObj(Daban daban){
		mDaban = daban;
	}
	
	@Override
	public void chuanyi(){
		if (mDaban != null){
			mDaban.chuanyi();
		}
	}
}

class Jeans extends DecoratorClient {
	@Override
	public void chuanyi(){
		System.out.println("穿牛仔裤");
		super.chuanyi();
	}
}

class Shoes extends DecoratorClient {
	@Override
	public void chuanyi(){
		System.out.println("穿鞋子");
		super.chuanyi();
	}
}

class Hat extends DecoratorClient {
	@Override
	public void chuanyi(){
		System.out.println("戴帽子");
		super.chuanyi();
	}
}



public class Decorator {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		
		Man p = new Man("Kevin");
		Jeans j = new Jeans();
		Shoes s = new Shoes();
		Hat h = new Hat();
		
		j.decoratorObj(p);
		s.decoratorObj(j);
		h.decoratorObj(s);
		h.chuanyi();
		

	}

}

适配器模式

将两种完全不同的事物联系到一起,就像现实生活中的变压器。假设一个手机充电器需要的电压是20V,但是正常的电压是220V,这时候就需要一个变压器,将220V的电压转换成20V的电压,这样,变压器就将20V的电压和手机联系起来了。

根据适配器类与适配者类的关系不同,适配器模式可分为对象适配器和类适配器两种,
对象适配器模式中,适配器与适配者之间是关联关系;
类适配器模式中,适配器与适配者之间是继承(或实现)关系。

角色
Target(目标抽象类):目标抽象类定义客户所需接口,可以是一个抽象类或接口,也可以是具体类。

Adapter(适配器类):适配器可以调用另一个接口,作为一个转换器,对Adaptee和Target进行适配,适配器类是适配器模式的核心,在对象适配器中,它通过继承Target并关联一个Adaptee对象使二者产生联系。

Adaptee(适配者类):适配者即被适配的角色,它定义了一个已经存在的接口,这个接口需要适配,适配者类一般是一个具体类,包含了客户希望使用的业务方法,在某些情况下可能没有适配者类的源代码。

优点

更好的复用性:系统需要使用现有的类,而此类的接口不符合系统的需要。那么通过适配器模式就可以让这些功能得到更好的复用。

更好的扩展性:在实现适配器功能的时候,可以调用自己开发的功能,从而自然地扩展系统的功能。

缺点

过多的使用适配器,会让系统非常零乱,不易整体进行把握。比如,明明看到调用的是A接口,其实内部被适配成了B接口的实现,一个系统如果太多出现这种情况,无异于一场灾难。因此如果不是很有必要,可以不使用适配器,而是直接对系统进行重构。

package javatest;

//设计模式-适配器模式
//类适配器
//已存在的将被适配的类(希望使用的方法)
class Adaptee {
    public void adapteeRequest() {
        System.out.println("被适配者的方法"); 
    }
}

//目标接口(现有的类只能实现这个接口)
interface Target {
    void request();
}

//现有的类
class Real implements Target{
	@Override
	public void request(){
		System.out.println("自己的方法"); 
	}
}

//定义一个适配器,同时实现Target接口和继承Adaptee类,然后在实现的 request() 方法中调用父类的 adapteeRequest()
class MakeAdapter extends Adaptee implements Target{
    @Override
    public void request() {
        //...一些操作...
        super.adapteeRequest();
        //...一些操作...
    }
}

//对象适配器(注意这里的 MakeAdapter 是将 Adaptee 作为一个成员属性,而不是继承它)
/*
class MakeAdapter implements Target{
    // 适配者是对象适配器的一个属性
    private Adaptee adaptee = new Adaptee();

    @Override
    public void request() {
        //...
        adaptee.adapteeRequest();
        //...
    }
}

 */


public class Adapter {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
			Target r = new Real();
			r.request();
			//目标接口的对象也可以调用适配者的方法了
			Target makeA = new MakeAdapter();
			makeA.request();

	}

}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值