Java设计模式(方法工厂类、单例模式、代理模式、策略模式、适配器、观察者、装饰类等)

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

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

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

 

本文选取部分常用的设计模式

 

一、简单工厂模式(Factory Method)

 

一些容易变化的地方,考虑用一个单独的类来做这个实例化的过程,这就是工厂。

先来看看它的组成:

1) 工厂类角色:这是本模式的核心,含有一定的商业逻辑和判断逻辑。在java中它往往由 一个具体类实现。

2) 抽象产品角色:它一般是具体产品继承的父类或者实现的接口。在java中由接口或者抽 象类来实现。

3) 具体产品角色:工厂类所创建的对象就是此角色的实例。在java中由一个具体类实现Java设计模式(方法工厂类、单例模式、代理模式、策略模式、适配器、观察者、装饰类等)_第1张图片

//抽象产品角色
public interface Car {
	public void drive();
}
//具体产品角色
public class Benz implements Car {

	@Override
	public void drive() {
		System.out.println("Driving Benz");
	}
}
//具体产品角色
public class Bmw implements Car {

	@Override
	public void drive() {
		System.out.println("Driving Bmw");
	}
}
//工厂类角色
public class Driver {
	//注意:返回类型为抽象产品角色
	public static Car driveCar(String s)throws Exception{
		//判断逻辑,返回具体的产品角色给Client
		if(s.equalsIgnoreCase("Benz")) {
			return new Benz();
		}else if(s.equalsIgnoreCase("Bmw")) {
			return new Bmw();
		}else {
			throw new Exception();
		}
	}
}
/**
 * 简单工厂模式:
 * 		包含的接口和类为:Car、Benz、Bmw、Driver
 * @author Mona
 *
 */

//暴发户
public class MagNate {
	public static void main(String[] args) {
		try {
			//告诉司机我今天坐奔驰
			Car car = Driver.driveCar("Benz");
			//下命令,开车
			car.drive();
		} catch (Exception e) {
			
		}
	}
}

Java设计模式(方法工厂类、单例模式、代理模式、策略模式、适配器、观察者、装饰类等)_第2张图片

Java设计模式(方法工厂类、单例模式、代理模式、策略模式、适配器、观察者、装饰类等)_第3张图片

工厂模式适合:

        凡是出现了大量的产品需要创建,并且具有共同的接口时,可以通过工厂方法模式进行创建。

 

二、抽象工厂模式(Abstract Factory)

 

原来:

Java设计模式(方法工厂类、单例模式、代理模式、策略模式、适配器、观察者、装饰类等)_第4张图片

 

这个时候的客户端调用是这样的:

// 得到 Intel 的 CPU
CPUFactory cpuFactory = new IntelCPUFactory();
CPU cpu = intelCPUFactory.makeCPU();
 
// 得到 AMD 的主板
MainBoardFactory mainBoardFactory = new AmdMainBoardFactory();
MainBoard mainBoard = mainBoardFactory.make();
 
// 组装 CPU 和主板
Computer computer = new Computer(cpu, mainBoard);

抽象工厂模式的用意为:

给客户端提供一个接口,可以创建多个产品族中的产品对象,而且使用抽象工厂模式还要满足以下条件:

1) 系统中有多个产品族,而系统一次只可能消费其中一族产品。

2) 同属于同一个产品族的产品一起使用

使用抽象工厂:

Java设计模式(方法工厂类、单例模式、代理模式、策略模式、适配器、观察者、装饰类等)_第5张图片

 

这个时候,对于客户端来说,不再需要单独挑选 CPU厂商、主板厂商、硬盘厂商等,直接选择一家品牌工厂,品牌工厂会负责生产所有的东西,而且能保证肯定是兼容可用的。

public static void main(String[] args) {
    // 第一步就要选定一个“大厂”
    ComputerFactory cf = new AmdFactory();
    // 从这个大厂造 CPU
    CPU cpu = cf.makeCPU();
    // 从这个大厂造主板
    MainBoard board = cf.makeMainBoard();
      // 从这个大厂造硬盘
      HardDisk hardDisk = cf.makeHardDisk();
 
    // 将同一个厂子出来的 CPU、主板、硬盘组装在一起
    Computer result = new Computer(cpu, board, hardDisk);
}

当然,抽象工厂的问题也是显而易见的,比如我们要加个显示器,就需要修改所有的工厂,给所有的工厂都加上制造显示器的方法。这有点违反了对修改关闭,对扩展开放这个设计原则。

 

三、单例模式(Singleton)

 

一步步分析:

1、属性

2、方法------------不行 每一次执行都会产生一个过程 保证不了唯一性

3、构造方法------不行 私有 本身就是这个构造过程

4、块--------------不行 没有返回值 创建了对象也无法给别人使用

 

单例模式的实现:

1、私有的构造方法

2、私有的静态的当前类对象作为属性

3、公有的静态的方法返回当前类对象

 

        单例模式又叫做单态模式或者单件模式。在 GOF 书中给出的定义为:保证一个类仅有 一个实例,并提供一个访问它的全局访问点。单例模式中的“单例”通常用来代表那些本质上 具有唯一性的系统组件(或者叫做资源)。比如文件系统、资源管理器等等。

        单例模式的目的就是要控制特定的类只产生一个对象,当然也允许在一定情况下灵活的 改变对象的个数那么怎么来实现单例模式呢?一个类的对象的产生是由类构造函数来完成 的,如果想限制对象的产生,一个办法就是将构造函数变为私有的(至少是受保护的),使 得外面的类不能通过引用来产生对象;同时为了保证类的可用性,就必须提供一个自己的对 象以及访问这个对象的静态方法。

        现在对单例模式有了大概的了解了吧,其实单例模式在实现上是非常简单的——只有一 个角色,而客户则通过调用类方法来得到类的对象。

放上一个类图吧,这样更直观一些:

Java设计模式(方法工厂类、单例模式、代理模式、策略模式、适配器、观察者、装饰类等)_第6张图片

 

1、饿汉式

 

/**
 * 单例模式------ 1、饿汉式  ==   线程安全
 * 
 * 		单例:不是无例  ---在本类中的某个成员位置上创建唯一的一个对象
 * 
 * 在类被加载的时候实例化,这样多次加载会照成多次实例
 * 
 * @author Mona
 *
 */
public class EHanShiSingleton {
	//在自己内部定义自己一个实例 
	//注意这是 private 只供内部调用 
	private static EHanShiSingleton instance = new EHanShiSingleton();//直接new,立即加载
	//如上面所述,将构造函数设置为私有
	private EHanShiSingleton() {
	}
	
	//静态工厂方法,提供了一个供外部访问得到对象的静态方法
	public static EHanShiSingleton geInstance() {
		return instance;
	}
}

2、懒汉式

 

/**
 * 
 * 单例模式-----2、懒汉式
 * 
 * 	防止多线程环 境中产生多个实例
 *  使用了 同步处理,在反应速度上要比第一种慢一些。  
 * 
 * @author Mona
 *
 */
public class LazySingleton {
    // 和饿汉模式相比,这边不需要先实例化出来,注意这里的 volatile,它是必须的
	private static volatile LazySingleton instance = null;
	
	//设置为私有的构造函数
	private LazySingleton() {
	}
	
	/**
	 * 静态工厂方法-----(提供一个获取单个对象的方法给用户)
	 * 返回值  将对象返回出去
	 * 
	 * @return
	 */
	public static LazySingleton getInstance() {//将类对自己的实例化延迟到第一次被引用的时候。
		if(instance == null) {
			// 加锁
			synchronized (LazySingleton.class) {
				 这一次判断也是必须的,不然会有并发问题
				if(instance == null) {
					instance = new LazySingleton();
				}
			}
		}
		return instance;//引用类型
	}
}		

以上两种实现方式均失去了多态性,不允许被继承。

将构造函数设置为受保护的,这样允许被继承产生子类。

双重检查,指的是两次检查 instance 是否为 null。

volatile 在这里是需要的,希望能引起读者的关注。

很多人不知道怎么写,直接就在 getInstance() 方法签名上加上 synchronized,这就不多说了,性能太差。

3、嵌套类最经典,以后大家就用它吧

 

public class Singleton3 {
 
    private Singleton3() {}
    // 主要是使用了 嵌套类可以访问外部类的静态属性和静态方法 的特性
    private static class Holder {
        private static Singleton3 instance = new Singleton3();
    }
    public static Singleton3 getInstance() {
        return Holder.instance;
    }
}

 

 

注意,很多人都会把这个嵌套类说成是静态内部类,严格地说,内部类和嵌套类是不一样的,它们能访问的外部类权限也是不一样的。

由于在 java 中子类的构造函数的范围不能比父类的小,所以可能存在不守规则的客户 程序使用其构造函数来产生实例,造成单例模式失效。

 

//总结一个完美的单例模式
public class Singleton {  
  
    /* 私有构造方法,防止被实例化 */  
    private Singleton() {  
    }  
  
    /* 此处使用一个内部类来维护单例 */  
    private static class SingletonFactory {  
        private static Singleton instance = new Singleton();  
    }  
  
    /* 获取实例 */  
    public static Singleton getInstance() {  
        return SingletonFactory.instance;  
    }  
  
    /* 如果该对象被用于序列化,可以保证对象在序列化前后保持一致 */  
    public Object readResolve() {  
        return getInstance();  
    }  
} 

其实说它完美,也不一定,如果在构造函数中抛出异常,实例将永远得不到创建,也会出错。

 

四、代理模式(Proxy)

 

代理模式是最常使用的模式之一

既然说是代理,那就要对客户端隐藏真实实现,由代理来负责客户端的所有请求。当然,代理只是个代理,它不会完成实际的业务逻辑,而是一层皮而已,但是对于客户端来说,它必须表现得就是客户端需要的真实实现。

 

Java设计模式(方法工厂类、单例模式、代理模式、策略模式、适配器、观察者、装饰类等)_第7张图片

1、静态代理

        静态代理在使用时,需要定义接口或者父类,被代理对象与代理对象一起实现相同的接口或者是继承相同父类.

        调用的时候通过调用代理对象的方法来调用目标对象.

        需要注意的是,代理对象与目标对象要实现相同的接口,然后通过调用相同的方法来调用目标对象的方法.

 

/**
 * 接口
 * 
 * @author Mona
 *
 */
public interface UserDao {
	void save();
}
/**
 * 接口实现
 * 目标对象
 * 
 * @author Mona
 *
 */
public class UserDaoImpl implements UserDao{
	public void save() {
		System.out.println("-------已保存数据--------");
		
	}
}
/**
 * 代理对象,静态代理
 * 
 * @author Mona
 *
 */
public class UserDaoProxy {
	//接收保存目标对象
	private UserDaoImpl target;
	public UserDaoProxy(UserDaoImpl target) {
		this.target = target;
	}
	
	public void save() {
		System.out.println("开始事务...");
		target.save();//执行目标对象的方法
		System.out.println("提交事务...");
	}
}
/**
 * 测试类
 * 
 * @author Mona
 *
 */
public class ProxyAppTest {
	public static void main(String[] args) {
		//目标对象
		UserDaoImpl target = new UserDaoImpl();
		
		//代理对象,把目标对象传给代理对象,建立代理关系
		UserDaoProxy proxy = new UserDaoProxy(target);
		
		proxy.save();//执行的是代理的方法
	}
}

Java设计模式(方法工厂类、单例模式、代理模式、策略模式、适配器、观察者、装饰类等)_第8张图片

静态代理总结:

1.可以做到在不修改目标对象的功能前提下,对目标功能扩展.

2.缺点:

因为代理对象需要与目标对象实现一样的接口,所以会有很多代理类,类太多.同时,一旦接口增加方法,目标对象与代理对象都要维护.

如何解决静态代理中的缺点呢?

答案是可以使用动态代理方式

 

代理模式的应用场景:

如果已有的方法在使用的时候需要对原有的方法进行改进,此时有两种办法:

1、修改原有的方法来适应。这样违反了“对扩展开放,对修改关闭”的原则。

2、就是采用一个代理类调用原有的方法,且对产生的结果进行控制。这种方法就是代理模式。

使用代理模式,可以将功能划分的更加清晰,有助于后期维护!

 

2、动态代理

 

动态代理有以下特点:

1.代理对象,不需要实现接口

2.代理对象的生成,是利用JDK的API,动态的在内存中构建代理对象(需要我们指定创建代理对象/目标对象实现的接口的类型)

3.动态代理也叫做:JDK代理,接口代理

JDK中生成代理对象的API

代理类所在包:java.lang.reflect.Proxy

JDK实现代理只需要使用newProxyInstance方法,但是该方法需要接收三个参数,完整的写法是:

 

static Object newProxyInstance(ClassLoader loader, 
                Class[] interfaces,InvocationHandler h )

直接使用静态代理中的接口及接口实现类,目标对象UserDaoImpl也没有修改

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * 2、创建动态代理对象(JDK代理)
 * 动态代理不需要实现接口,但是需要指定接口类型
 * 
 * @author Mona
 *
 */
public class ProxyFactory {
	//维护一个目标对象
	private Object target;
	public ProxyFactory(Object target) {
		this.target = target;
	}

	//给定目标对象生成代理对象
	public Object getProxyInstance() {
		return Proxy.newProxyInstance(target.getClass().getClassLoader(), 
				target.getClass().getInterfaces(), 
				new InvocationHandler() {
					
					@Override
					public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
						System.out.println("开始事务2");
						return null;
					}
				});
	}
}
/**
 * 动态代理---测试类
 * 
 * @author Mona
 *
 */
public class ProxyTestDynamic {
	public static void main(String[] args) {
		//目标对象
		UserDao target = new UserDaoImpl();
		System.out.println(target.getClass());
		
		//给目标对象,创建代理对象
		UserDao proxy = (UserDao) new ProxyFactory(target).getProxyInstance();
		//内存中动态生成的代理对象
		System.out.println(proxy.getClass());
		
		//执行方法  【代理】
		proxy.save();
	}
}

总结:

代理对象不需要实现接口,但是目标对象一定要实现接口,否则不能用动态代理

 

3、Cglib

 

五、适配器模式(Adapter)

 

适配器模式定义:

         将一个类的接口转换成客户希望的另外 一个接口。Adapter 模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。

 

装饰器模式的应用场景:

1、需要扩展一个类的功能。

2、动态的为一个对象增加功能,而且还能动态撤销。(继承不能做到这一点,继承的功能是静态的,不能动态增删。)

缺点:产生过多相似的对象,不易排错!

 

Java设计模式(方法工厂类、单例模式、代理模式、策略模式、适配器、观察者、装饰类等)_第9张图片

添加了圆的绘制以后的类结构:

Java设计模式(方法工厂类、单例模式、代理模式、策略模式、适配器、观察者、装饰类等)_第10张图片

/**
 * 对象适配器-----《Head First 设计模式》中的一个例子
 * 
 * @author Mona
 *
 */
public interface AdapterDuck {
	public void quack();//鸭子的呱呱叫
	public void fly();//飞	
}
//鸡
public interface AdapterCock {
	public void gobble();//鸡的咕咕叫
	public void fly();//飞
}
//野鸡
public class WildCock implements AdapterCock{
	public void gobble() {
		System.out.println("咕咕叫");
	}
	
	public void fly() {
		System.out.println("鸡也会飞哦");
	}
}

 

/**
 * 适配器
 * 
 * @author Mona
 *
 */
public class AdapterCockAdapter implements AdapterDuck{
	AdapterCock cock;
	
	// 构造方法中需要一个鸡的实例,此类就是将这只鸡适配成鸭来用
	public AdapterCockAdapter(AdapterCock cock) {
		this.cock = cock;
	}
	
	//实现鸭的咕咕叫方法
	@Override
	public void quack() {
		cock.gobble();
	}
	
	@Override
	public void fly() {
		cock.fly();
	}
}
//客户端测试
public class AdapterTest {
	public static void main(String[] args) {
		//有一只野鸭
		AdapterCock wildCock = new WildCock();
		
		//成功将野鸡适配成为鸭
		AdapterDuck duck = new AdapterCockAdapter(wildCock);
		
		//...
	}
}

 

六、策略模式(简单&常用)(Strategy)

 

策略模式(Strategy)属于对象行为型设计模式,主要是定义一系列的算法,把这些算 法一个个封装成拥有共同接口的单独的类,并且使它们之间可以互换。

它将算法的使用和算法本身分离,即将变化的具体算法封装了起来,降低了代码的耦合 度,系统业务策略的更变仅需少量修改。

策略模式由三个角色组成:

1) 算法使用环境(Context)角色:算法被引用到这里和一些其 ,它的与环境有关的操作一起 来完成任务。

2) 抽象策略(Strategy)角色:规定了所有具体策略角色所需的接口。在 java 它通常由接口 或者抽象类来实现。

3) 具体策略(Concrete Strategy)角色:实现了抽象策略角色定义的接口。 策略模式各个角色之间关系的类图表示:

Java设计模式(方法工厂类、单例模式、代理模式、策略模式、适配器、观察者、装饰类等)_第11张图片

/**
 * 策略接口
 * 
 * @author Mona
 *
 */
public interface Strategy {
	public void doWork();
}
/**
 * 具体策略角色1
 * 
 * @author Mona
 *
 */
public class ConcreteStrategy1 implements Strategy{
	@Override
	public void doWork() {
		System.out.println("具体策略1工作");
	}
}
/**
 * 具体策略角色2
 * 
 * @author Mona
 *
 */
public class ConcreteStrategy2 implements Strategy{
	@Override
	public void doWork() {
		System.out.println("具体策略2工作");
	}
}
/**
 * 使用策略的类---封装角色
 * 
 * @author Mona
 *
 */
public class ContextStrategy {

	//抽象策略
	private Strategy mStrategy = null;
 
	public ContextStrategy(Strategy strate) {
		this.mStrategy = strate;
	}
 
	//封装后,策略方法
	public void onAnyWork() {
		this.mStrategy.doWork();
	}
}
/**
 * 策略模式测试
 * 
 * @author Mona
 *
 */
public class StrategyTest {
	public static void main(String[] args) {
		Strategy mStrategy = new ConcreteStrategy1();
		// 声明上下文
		ContextStrategy context = new ContextStrategy(mStrategy);
		// 执行封装后的方法
		context.onAnyWork();
		context = new ContextStrategy(new ConcreteStrategy2());
		// 执行封装后的方法
		context.onAnyWork();
	}
}

 

策略模式的决定权在用户,系统本身提供不同算法的实现,新增或者删除算法,对各种算法做封装。因此,策略模式多用在算法决策系统中,外部用户只需要决定用哪个算法即可。

 

七、观察者模式(面试时手写)(Observer)

 

Java设计模式(方法工厂类、单例模式、代理模式、策略模式、适配器、观察者、装饰类等)_第12张图片

        定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。

        观察者模式对于我们来说,真是再简单不过了。无外乎两个操作,观察者订阅自己关心的主题和主题有数据变化后通知观察者们。

        首先,需要定义主题,每个主题需要持有观察者列表的引用,用于在数据变更的时候通知各个观察者:

 

public class Subject {
 
   private List observers = new ArrayList();
   private int state;
 
   public int getState() {
      return state;
   }
 
   public void setState(int state) {
      this.state = state;
      // 数据已变更,通知观察者们
      notifyAllObservers();
   }
 
   public void attach(Observer observer){
      observers.add(observer);        
   }
 
   // 通知观察者们
   public void notifyAllObservers(){
      for (Observer observer : observers) {
         observer.update();
      }
   }     
}

定义观察者接口:

public abstract class Observer {
   protected Subject subject;
   public abstract void update();
}

其实如果只有一个观察者类的话,接口都不用定义了,不过,通常场景下,既然用到了观察者模式,我们就是希望一个事件出来了,会有多个不同的类需要处理相应的信息。比如,订单修改成功事件,我们希望发短信的类得到通知、发邮件的类得到通知、处理物流信息的类得到通知等。

我们来定义具体的几个观察者类:

public class BinaryObserver extends Observer {
 
      // 在构造方法中进行订阅主题
    public BinaryObserver(Subject subject) {
        this.subject = subject;
        // 通常在构造方法中将 this 发布出去的操作一定要小心
        this.subject.attach(this);
    }
 
      // 该方法由主题类在数据变更的时候进行调用
    @Override
    public void update() {
        String result = Integer.toBinaryString(subject.getState());
        System.out.println("订阅的数据发生变化,新的数据处理为二进制值为:" + result);
    }
}
 
public class HexaObserver extends Observer {
 
    public HexaObserver(Subject subject) {
        this.subject = subject;
        this.subject.attach(this);
    }
 
    @Override
    public void update() {
          String result = Integer.toHexString(subject.getState()).toUpperCase();
        System.out.println("订阅的数据发生变化,新的数据处理为十六进制值为:" + result);
    }
}

客户端使用也非常简单:

public static void main(String[] args) {
    // 先定义一个主题
      Subject subject1 = new Subject();
      // 定义观察者
      new BinaryObserver(subject1);
      new HexaObserver(subject1);
 
      // 模拟数据变更,这个时候,观察者们的 update 方法将会被调用
      subject.setState(11);
}

output:

订阅的数据发生变化,新的数据处理为二进制值为:1011
订阅的数据发生变化,新的数据处理为十六进制值为:B

八、模板方法模式(Template Method)

 

        一个抽象类中,有一个主方法,再定义1...n个方法,可以是抽象的,也可以是实际的方法,定义一个类,继承该抽象类,重写抽象方法,通过调用抽象类,实现对子类的调用。

        1) 抽象类(Abstract Class):定义了一到多个的抽象方法,以供具体的子类来实现它们; 而且还要实现一个模板方法,来定义一个算法的骨架。

        该模板方法不仅调用前面的抽象方法,也可以调用其他的操作,只要能完成自身的使命。

        2) 具体类(Concrete Class):实现父类中的抽象方法以完成算法中与特定子类相关的步骤。

 

Java设计模式(方法工厂类、单例模式、代理模式、策略模式、适配器、观察者、装饰类等)_第13张图片

在含有继承结构的代码中,模板方法模式是非常常用的,这也是在开源代码中大量被使用的。

/**
 * 模板设计模式
 * 	模板方法只负责定义第一步应该要做什么,第二步应该做什么,第三步应该做什么,至于怎么做,由子类来实现。
 * 
 * @author Mona
 *
 */
public abstract class AbstractTemplate {
	//这是模板方法
	public void templateMethod() {
		init();
		apply();
		end();
	}
	
	protected void init() {
		System.out.println("init 抽象层已经实现,子类也可以选择覆盖");
	}
	
	//留给子类实现
	protected abstract void apply();	//apply() 是抽象方法,子类必须实现它
	protected void end() {
	}
}
/**
 * 模板方法的一个实现类
 * 
 * @author Mona
 *
 */
public class ConcreteTemplate extends AbstractTemplate{
	@Override
	public void apply() {
		System.out.println("子类实现抽象方法 apply");
	}
	
	@Override
	public void end() {
		System.out.println("我们可以把 method3 当作钩子方法来使用,需要的时候覆盖就可以了");
	}
}
public class TemplateTest {
	public static void main(String[] args) {
		AbstractTemplate t = new ConcreteTemplate();
		//调用模板方法
		t.templateMethod();
	}
}

/**
 * JAVA 通过父类对象new 子类对象,这个对象的声明的类型就是父类的类型,
 * 调用这个对象的方法也只能是父类型的方法,子类独有的方法是不能够被使用的。
 * 
 * 例如 :
 *         List alist =new ArrayList<>();//只能用List中的方法
 * 	ArrayList arrayList=new ArrayList<>();//可以用arraylsit独有的属性和方法 
 * 
 * 面向接口编程的思想:
 * 		之所以要用父类来new子类,而不是直接用子类 new 子类,
 * 		是因为假如以后要重构代码,
     *		把ArrayList换成LinkedList  那么采用这种方式,
 * 		只需要修改一行代码即可,若是直接new
 * 		那么后面的所有用到的 ArrayList 的地方就都要改动
 * 
 */

很简单,一看就懂,重要的是熟练使用。

 

适用情况 :

根据上面对定义的分析,以及例子的说明,可以看出模板方法适用于以下情况:

        1) 一次性实现一个算法的不变的部分,并将可变的行为留给子类来实现。

        2) 各子类中公共的行为应被提取出来并集中到一个公共父类中以避免代码重复。其实这可以说是一种好的编码习惯了。

        3) 控制子类扩展。模板方法只在特定点调用操作,这样就只允许在这些点进行扩展。

        如果你不愿子类来修改你的模板方法定义的框架,你可以采用两种方式来做:一是在 API 中不体现出你的模板方 法;或者将你的模板方法置为 final 就可以了。

        可以看出,使用模板方法模式可以将代码的公共行为提取出来,达到复用的目的。而且, 在模板方法模式中,是由父类的模板方法来控制子类中的具体实现。这样你在实现子类的时 候,根本不需要对业务流程有太多的了解

 

九、装饰者模式(Decorator)

 

动态地给一个对象添加一些额外的职责。

 

Java设计模式(方法工厂类、单例模式、代理模式、策略模式、适配器、观察者、装饰类等)_第14张图片

/**
 * 装饰者模式
 * 	定义饮料抽象类
 * 
 * @author Mona
 *
 */
public abstract class Beverage {
	//返回描述
	public abstract String getDescription();
	//返回价格
	public abstract double cost();
}
/**
 * 三个基础饮料实现类,红茶、绿茶和咖啡
 * 
 * @author Mona
 *
 */
public class BlackTea extends Beverage{
	public String getDescription() {
		return "红茶";
	}
	
	public double cost() {
		return 10;
	}
}
/**
 * 绿茶
 * 
 * @author Mona
 *
 */
public class GreenTea extends Beverage{
	public String getDescription() {
		return "绿茶";
	}
	
	public double cost() {
		return 11;
	}
}
/**
 * 定义调料,也就是装饰者的基类,此类必须继承自Beverage
 * 
 * @author Mona
 *
 */
public abstract class Condiment extends Beverage{
}
/**
 * 定义柠檬、芒果等具体的调料,它们属于装饰者,这些调料肯定都需要继承Condiment类
 * 
 * @author Mona
 *
 */
public class Lemon extends Condiment{
	private Beverage beverage;
	 这里很关键,需要传入具体的饮料,如需要传入没有被装饰的红茶或绿茶,
    // 当然也可以传入已经装饰好的芒果绿茶,这样可以做芒果柠檬绿茶
	public Lemon(Beverage beverage) {
		this.beverage = beverage;
	}
	
	public String getDescription() {
		//装饰
		return beverage.getDescription() + ",加柠檬";
	}
	
	public double cost() {
		//装饰
		return beverage.cost() + 2;//加柠檬需要2元
	}
}
/**
 * 芒果
 * 
 * @author Mona
 *
 */
public class Mongo extends Condiment{
	private Beverage beverage;
	
	public Mongo(Beverage beverage) {
		this.beverage = beverage;
	}
	
	public String getDescription() {
		return beverage.getDescription() + ",加芒果";
	}
	
	public double cost() {
		return beverage.cost() + 3;
	}
}

装饰器模式的应用场景:

1、需要扩展一个类的功能。

2、动态的为一个对象增加功能,而且还能动态撤销。(继承不能做到这一点,继承的功能是静态的,不能动态增删。)

缺点:产生过多相似的对象,不易排错!

 

十、建造者模式(Builder)

 

将构造复杂对象的过程和组成对象的部件解耦。

 

Java设计模式(方法工厂类、单例模式、代理模式、策略模式、适配器、观察者、装饰类等)_第15张图片

/**
 * 建造者模式
 * 
 * @author Mona
 *
 */
class User{
	private String name;
	private String password;
	private String nickName;
	private int age;
	
	//构造方法私有化,不然客户端就会直接调用构造方法了
	private User(String name,String password,String nickName,int age) {
		this.name = name;
		this.password = password;
		this.nickName = nickName;
		this.age = age;
	}
	
	//静态方法,用于生成一个Builder,这个不一定要有,不够写这个方法是一个很好的习惯
	//有些代码要求别人写new User.UserBuilder().a()...build()栏上去就没有那么好
	public static UserBuilder builder() {
		return new UserBuilder();
	}
	
	public static class UserBuilder{
		private String name;
		private String password;
		private String nickName;
		private int age;
		
		private UserBuilder() {
		}
		
		public UserBuilder name(String name) {
			this.name = name;
			return this;
		}
		
		public UserBuilder password(String password) {
			this.password = password;
			return this;
		}
		
		public UserBuilder nickName(String nickName) {
			this.nickName = nickName;
			return this;
		}
		
		public UserBuilder age(int age) {
			this.age = age;
			return this;
		}
		
		public User build() {
			if(name == null || password == null) {
				throw new RuntimeException("用户名和密码必填");
			}
			if(age <= 0 || age >= 150) {
				throw new RuntimeException("年龄不合法");
			}
			if(nickName == null) {
				nickName = name;
			}
			return new User(name, password, nickName, age);
		}
	}
}

public class BuilderUser {
	public static void main(String[] args) {
		User d = User.builder().name("foo").password("pass12345").age(25).build();
		
	}
}

 

参考文章:https://blog.csdn.net/a724888/article/details/71104023

https://blog.csdn.net/qq_38024548/article/details/80480831

 

 

你可能感兴趣的:(Java基础)