设计模式

208 阅读12分钟

Java 中一般认为有 23种设计模式,我们不需要所有的都会,但是其中常用的几种设计模式应该去掌握。下面列出了所有的设计模式。需要掌握的设计模式我单独列出来了,当然能掌握的越多越好。

总体来说设计模式分为三大类:

创建型模式,共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。

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

行为型模式,共十一种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。

1.单例模式

package com.dzj.design;
/**
 * 饿汉式单利:线程安全
 * */
public class Singleton {
	 //直接创建对象
     private static final Singleton singleton = new Singleton();
     //限制产生多个对象
     private Singleton(){
    	 
     }
     //通过该方法获得实例对象
     public static Singleton getInstance(){
    	 return singleton;
     }
     //类中其他方法,尽量是static
     public static void doSometing(){
    	 
     }
}

懒汉式

package com.dzj.design;

/**
 * 在高并发情况下,注意单例模式的线程同步问题。 单例模式有几种实现方式:懒汉式模式 线程不安全
 **/
 
public class NoSecuritySingleton {
	private static NoSecuritySingleton singleton = null;

	// 限制产生多个对象
	private NoSecuritySingleton() {

	}

	// 通过该方法获得实例对象
	public static NoSecuritySingleton getSingleton() {
		if (singleton == null) {
			singleton = new NoSecuritySingleton();
		}
		return singleton;

	}

}

 /**
	 * 该单例模式在低并发的情况下尚不会出现问题,若系统压力增大,并发量增加时则可能
	 * 在内存中出现多个实例,破坏了最初的预期。原因:如一个线程A执行到singleton =new NoSecuritySingleton(),但是还没
	 * 有获得对象(对象初始化需要时间),第二个线程B也在执行,执行到singleton==null判断,那么线程B获得判断条件也为真,于是继续运行下去,
	 * ,线程A获得一个对象,线程B也获得一个对象,在内存中就出现两个对象。
	 * 解决办法:在getSingleton()方法前加synchronized关键字或者在getSingleton()方法内增加synchronized实现。
	 * 
	 * **/

工厂设计模式

例如:一个系统,按钮工厂类可以返回一个具体的按钮实例,如圆形按钮、矩形按钮、菱形按钮等,在这个系统中,如果需要增加一种新类型的按钮,那、除了增加一个新的具体产品类之外,还需要修改工厂类的代码,这就使得整个设计在一定程度上违反了“开闭原则”。

改进

不再设计一个按钮工厂类来统一负责所有产品的创建,而是将具体按钮的创建过程交给专门的工厂子类去完成。先定义一个抽象的按钮工厂类,再定义具体的工厂类来生成圆形按钮、矩形按钮、菱形按钮等。它们实现在抽象按钮工厂类中定义的方法。

定义

在工厂模式中,工厂父类负责定义创建产品对象的公共接口,而工厂子类则负责生成具体的产品对象

目的

将产品类的实例化操作延迟到工厂子类中完成,即通过工厂子类来确定究竟应该实例化哪一个具体产品类。

模式分析:在现实生活中的手机工厂,不同品牌的手机应该由不同的公司制造,Motorola公司生产Motorola手机,Nokia公司生产Nokia手机,那么抽象层的手机公司生产抽象的手机,而具体的手机公司就生产具体品牌的手机,其中就蕴含了工厂方法模式的应用。

别名:多态性工厂模式---具体工厂类都有共同的接口,或者有共同的抽象父类。当系统扩展需要添加新的产品对象时,仅仅需要添加一个具体产品对象以及一个具体工厂对象,原有工厂对象不需要进行任何修改,也不需要修改客户端,很好符合了“开闭原则”。而简单工厂模型在添加新产品对象后不得不修改工厂方法,扩展性不好。

实例1:可以将原有的工厂进行分割,为每种品牌的电视机提供一个子工厂,海尔工厂专门负责生产海尔电视机,海信工厂专门负责生产海信电视机,如果需要生产TCL电视机或创维电视机,只需要对应增加一个新的TCL工厂或者创建工厂即可,原有的工厂无需做任何修改,使得整个系统具有更好的灵活性和可扩展性。

实例2:某系统日志记录器要求支持多种日志记录方式,如文件记录,数据库记录等,且用户可以根据要求动态选择日志记录方式。

模式优点:

使用工厂方法在系统中加入新产品时,无须修改抽象工厂和抽象产品提供的接口,无须修改客户端,也无须修改其他的具体工厂和具体产品,而只要添加一个具体工厂和具体产品就可以。

基于工厂角色和产品角色的多态性设计是工厂方法模式的关键。它能够使工厂可以自主确定创建何种产品对象,而如何创建这个对象的细节则完全封装在具体工厂内部。工厂方法模式之所以被称为多态工厂模式是因为所有的具体工厂类都具有同一抽象父类。

工厂方法用来创建客户所需要的产品,同时还向客户隐藏了哪种具体产品类被实例化这一细节,用户只需要关心所需产品对应的工厂,无须关心创建细节,甚至无须知道具体产品类的类名。

模式缺点

在添加新产品时,需要编写新的具体产品类,而且还要提供与之对应的具体工厂类,系统中类的个数将成对增加,在一定程度上增加了系统的复杂度,有更多的类需要编译和运行,会给系统带来一些额外的开销。

由于考虑到系统的可扩展性,需要引入抽象层,在客户端代码中均使用抽象层进行定义,增加了系统的抽象性和理解难度,且在实现时可能需要到DOM、反射等技术,增加了系统的复杂度。

模式的应用

在java集合框架中,常用的List和Set等集合等继承(或实现)了java.util Collection接口, 在Colection接口中为所有的java集合类定义了一个iterator()方法,可返回一个用于遍历集合的Iterator(迭代器)类型的对象。

java消息服务JMS定义了一套标准的API,让java语言程序能够通过支持JMS标准的mom来创建和交换信息。在JMS的实现过程中就需要广泛使用到工厂方法模式,应用于创建Connection连接对象,创建session会话对象,创建Sender消息发送等。

示例

public interface Sender {
	public void Send();
}
public class MailSender implements Sender{
	@Override
	public void Send() {
		System.out.println("this is mail sender!");
	}
}
public class SmsSender implements Sender{
	@Override
	public void Send() {
		System.out.println("this is sms sender!");
	}

}
public class SendFactory {
	public Sender produce(String type) {
		if ("mail".equals(type)) {
			return new MailSender();
		} else if ("sms".equals(type)) {
			return new SmsSender();
		} else {
			System.out.println("请输入正确的类型!");
			return null;
		}
	}
}
多个工厂方法模式

该模式是对普通工厂方法模式的改进,在普通工厂方法模式中,如果传递的字符串出错,则不能正确创建对象,而多个工厂方法模式是提供多个工厂方法,分别创建对象。

public class SendFactory {
	public Sender produceMail() {
		return new MailSender();
	}

	public Sender produceSms() {
		return new SmsSender();
	}
}

测试:

public class FactoryTest {
		public static void main(String[] args) {
			SendFactory factory = new SendFactory();
			Sender sender = factory.produceMail();
			sender.send();
		}
}
静态工厂方法模式

将上面的多个工厂方法模式里的方法置为静态的,不需要创建实例,直接调用即可。

public static class SendFactory {
	public Sender produceMail() {
		return new MailSender();
	}

	public Sender produceSms() {
		return new SmsSender();
	}
}
public class FactoryTest {
		public static void main(String[] args) {
			SendFactory.produceMail();
	}
}
抽象工厂模式

工厂方法模式有一个问题就是,类的创建依赖工厂类,也就是说,如果想要拓展程序,必须对工厂类进行修改,这违背了闭包原则,所以,从设计角度考虑,有一定的问题,如何解决?就用到抽象工厂模式,创建多个工厂类,这样一旦需要增加新的功能,直接增加新的工厂类就可以了,不需要修改之前的代码。

public interface Provider {
   public Sender produce();
}
public interface Sender {
   public void send();
}
public class MailSender implements Sender {

    @Override
    public void send() {
    System.out.println("this is mail sender!");
}
}

public class SmsSender implements Sender {
    
    @Override
    public void send() {
    System.out.println("this is sms sender!");
}
}
 public class SendSmsFactory implements Provider {
    @Override
    public Sender produce() {
        return new SmsSender();
  }
 }
public class SendMailFactory implements Provider {
     @Override
    public Sender produce() {
        return new mailSender();
  }
    
}

测试:

public class Test {

public static void main(String[] args) {
    Provider provider = new SendMailFactory();
    Sender sender = provider.produce();
    sender.send();
} }

建造者模式

概念:

工厂类模式提供的是创建单个类的模式,而建造者模式则是将各种产品集中起来进行管理,用来创建复合对象,所谓复合对象就是指某个类具有不同的属性,其实建造者模式就是前面抽象工厂模式和最后的 Test 结合起来得到的。

CarModel类
package com.dzj.builder.partter;
import java.util.ArrayList;

public abstract class CarModel {
	// 这个参数是各个基本方法执行的顺序
	private ArrayList<String> sequence = new ArrayList<String>();

	/**
	 * 发动
	 */
	protected abstract void start();

	/**
	 * 能够停止
	 */
	protected abstract void stop();

	/**
	 * 可以按喇叭
	 */
	protected abstract void alarm();

	/**
	 * 引擎会轰隆隆的响
	 */
	protected abstract void engineBoom();

	final public void run() {
		// 循环,谁在前,就先执行
		for (int i = 0; i < this.sequence.size(); i++) {
			String actionName = this.sequence.get(i);
			if (actionName.equalsIgnoreCase("start")) {
				this.start();
			} else if (actionName.equalsIgnoreCase("stop")) {
				this.stop();
			} else if (actionName.equalsIgnoreCase("alarm")) {
				this.alarm();
			} else if (actionName.equalsIgnoreCase("engine boom")) {
				// 如果是engine boom 关键字
				this.engineBoom();
			}
		}
	}
	//把传递过来的值传递到类内
	final public void setSequence(ArrayList<String> sequence){
		this.sequence = sequence;
	}
}

BenzModel类
package com.dzj.builder.partter;

public class BenzModel extends CarModel{

	@Override
	protected void alarm() {
		System.out.println("悍马H1鸣笛。。。");
	}

	@Override
	protected void engineBoom() {
		System.out.println("悍马H1引擎声音。。。");
	}

	@Override
	protected void start() {
		System.out.println("悍马H1发动。。。");
	}

	@Override
	protected void stop() {
		System.out.println("悍马H1停车。。。");
	}
      
}

BenzBuilder类:
package com.dzj.builder.partter;
import java.util.ArrayList;

/**
 * 给定一个汽车的顺序,然后返回一辆奔驰车
 */
public class BenzBuilder extends CarBuilder {
	private BenzModel benz = new BenzModel();

	@Override
	public void setSequence(ArrayList<String> sequence) {
		this.benz.setSequence(sequence);
	}

	@Override
	public CarModel getCarModel() {

		return this.benz;
	}

}

CarBuilder类:
package com.dzj.builder.partter;
import java.util.ArrayList;
/**
 * 抽象汽车组装者
 * 增加一个CarBuilder抽象类,由它组装各个车模,要什么类型什么顺序的车辆模型,由相关子类完成
 * */
public abstract class CarBuilder {
	// 建造一个模型,你要给我一个顺序要,就是组装顺序
	public abstract void setSequence(ArrayList<String> sequence);
	//设置完毕顺序后,就可以直接拿到这个车辆模型
	public abstract CarModel getCarModel();
}

Client类:
package com.dzj.builder.partter;
import java.util.ArrayList;

/**
 * 场景类
 * */
public class Client {
   public static void main(String[] args) {
	  /**
	   * 客户告诉公司,我要这样一个模型,然后xx公司就告诉老大
	   * */
	   //存放run的顺序
	   ArrayList<String> sequence = new ArrayList<String>();
	   sequence.add("engine boom");
	   sequence.add("start");
	   sequence.add("stop");
	   //要一个奔驰
	   BenzBuilder benzBuilder = new BenzBuilder();
	   //把顺序给这个builder类,制造出这样一个车出来
	   benzBuilder.setSequence(sequence);
	   BenzModel benz = (BenzModel)benzBuilder.getCarModel();
	   benz.run();
}
}

适配器模式

适配器模式将某个类的接口转换成客户端期望的另一个接口表示,目的是消除由于接口不匹配所造成的类的兼容性问题。主要分为三类:类的适配器模式、对象的适配器模式、接口的适配器模式。

类的适配器模式

package com.dzj.adapter;
public class Source {
	public void method1() {
		System.out.println("this is original method!");
	}
}
package com.dzj.adapter;
public interface Targetable {
	//与原类中的方法相同
	public void method1();
    //新类的方法
	public void method2();
}

Adapter类

package com.dzj.adapter;
public class Adapter extends Source implements Targetable{

	@Override
	public void method2() {
		System.out.println("this is the targetable method!");
	}

}
package com.dzj.adapter;

public class AdapterTest {
	public static void main(String[] args) {
		Targetable adapter = new Adapter();
		adapter.method1();
		adapter.method2();
	}
}

对象的适配器模式

基本思路和类的适配器模式相同,只是将 Adapter 类作修改,这次不继承 Source 类,而是持有 Source 类的实例,以达到解决兼容性的问题。

source类
package com.dzj.adapter;
public class Source {
	public void method1() {
		System.out.println("this is original method!");
	}
}
Wrapper类
package com.dzj.adapter;

public class Wrapper implements Targetable{
   
	private Source source;
	
	public Wrapper(Source source) {
		super();
		this.source = source;
	}
	
	@Override
	public void method1() {
		source.method1();
	}

	@Override
	public void method2() {
		System.out.println("this is the targetable method!");	
	}

}

AdapterTest类
package com.dzj.adapter;

public class AdapterTest {
	public static void main(String[] args) {
		   Source source = new Source();
		   Targetable target = new Wrapper(source);
		   target.method1();
		   target.method2();
	}
}

接口的适配器模式

接口的适配器是这样的:有时我们写的一个接口中有多个抽象方法,当我们写该接口的实现类时,必须实现该接口的所有方法,这明显有时比较浪费,因为并不是所有的方法都是我们需要的,有时只需要某一些,此处为了解决这个 问题,我们引入了接口的适配器模式,借助于一个抽象类,该抽象类实现了该接口,实现了所有的方法,而我们不和原始的接口打交道,只和该抽象类取得联系,所以我们写一个类,继承该抽象类,重写我们需要的方法就行

装饰模式(Decorator)

顾名思义,装饰模式就是给一个对象增加一些新的功能,而且是动态的,要求装饰对象和被装饰对象实现同一个接口,装饰对象持有被装饰对象的实例。

Sourceable
package com.dzj.decorator;

public interface Sourceable {
	public void method();
}
Source类
package com.dzj.decorator;

public class Source implements Sourceable{

	@Override
	public void method() {
		System.out.println("the original method!");
	}

}
Decorator类
package com.dzj.decorator;

public class Decorator implements Sourceable {

	private Sourceable source;

	public Decorator(Sourceable source) {
		super();
		this.source = source;
	}

	@Override
	public void method() {
		System.out.println("before decorator!");
	    source.method();
	    System.out.println("after decorator!");
	}
}

观察者模式

观察者模式很好理解,类似于邮件订阅和 RSS 订阅,当我们浏览一些博客或 wiki 时,经常会看到RSS图标,就这的意思是,当你订阅了该文章,如果后续有更新,会及时通知你。简单来讲就一句话:当一个对象变化时,其它依赖该对象的对象都会收到通知,并且随着变化!对象之间是一种一对多的关系。

Observer
package com.dzj.observer;
public interface Observer {
   public void update();
}

package com.dzj.observer;
public class Observer1 implements Observer{

	@Override
	public void update() {
		System.out.println("observer1 has received!");		
	}
}

package com.dzj.observer;
public class Observer2 implements Observer{
	@Override
	public void update() {
		System.out.println("observer2 has received!");		
	}
}

Subject
package com.dzj.observer;

public interface Subject {
	//增加观察者
	public void add(Observer observer);
	
	//修改观察者
	public void del(Observer observer);
	
	//通知所有的观察者
	public void notifyObservers();
	
	//自身的操作
	public void operation();
	
}
AbstractSubject类
package com.dzj.observer;

import java.util.Enumeration;
import java.util.Vector;

public abstract class AbstractSubject implements Subject{
	Vector<Observer> vector = new Vector<Observer>();
	
	@Override
	public void add (Observer observer){
	   vector.add(observer);
	}
	
	@Override
	public void del(Observer observer){
	   vector.remove(observer);
	}
	
	@Override
	public void notifyObservers(){
		Enumeration<Observer> elements = vector.elements();
		while (elements.hasMoreElements()){
			elements.nextElement().update();
		}
	}
	
}

MySubject
package com.dzj.observer;

public class MySubject extends AbstractSubject{

	@Override
	public void operation() {
		System.out.println("update self!");
	    notifyObservers();
	}

}
package com.dzj.observer;
public class ObserverTest {
	public static void main(String[] args) {
		Subject sub = new MySubject();
	    sub.add(new Observer1());
	    sub.add(new Observer2());
	    sub.operation();
	}
}