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

目录

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

二、工厂方法模式

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

3.1 三个工厂模式区别:

四、单例模式(Singleton)

1、饿汉式

2、懒汉式

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

五、代理模式(Proxy)

1、静态代理

2、动态代理

3、Cglib

动态代理对比:

六、装饰者模式(Decorator)

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

7.1 代理模式、装饰器模式、策略模式的区别

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

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

十、适配器模式(Adapter)

todo: 待补充

十一、建造者模式(Builder)


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

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

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

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

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

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

先来看看它的组成:

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

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

3) 具体产品角色:工厂类所创建的对象就是此角色的实例。在java中由一个具体类实现

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

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

	@Override
	public void drive() {
		System.out.println("生产 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) {
			
		}
	}
}

二、工厂方法模式

工厂方法是针对每一种产品提供一个工厂类。通过不同的工厂实例来创建不同的产品实例。 在同一等级结构中,支持增加任意产品。

在上面代码的基础之上,再增加下面的代码:

生产汽车的工厂接口
public interface CarFactory {
    Car produce();
}
public class BenchiFactory implements CarFactory {
    @Override
    public Car produce() {
        return new Benchi();
    }
}
public class BwmFactory implements CarFactory {
    @Override
    public Car produce() {
        return new Bwm();
    }
}
public static void main(String[] args) {
    // 先创建 汽车工厂
    CarFactory benchiFactory = new BenchiFactory();
    benchiFactory.produce().drive();

    // 这个模式对于同一级别的产品,可扩展性高
    // 可以扩展不同品牌的汽车,此时不需要修改代码,只需要增加代码即可
    // 创建一个新的品牌汽车  宝马
    // new 另外的工厂  :  new BwmFactory();
}

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

抽象工厂模式的用意为:

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

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

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

迷你汽车接口
public interface MiniCar {
    void showInfo();
}
SUV汽车接口
public interface SUVCar {
    void showInfo();
}
public class AudiMiniCar implements MiniCar {
    @Override
    public void showInfo() {
        System.out.println("这是奥迪迷你汽车 ");
    }
}

public class BMWMiniCar implements  MiniCar {
    @Override
    public void showInfo() {
        System.out.println("这是宝马Cooper迷你汽车");
    }
}
public class AudiSUVCar implements  SUVCar {
    @Override
    public void showInfo() {
        System.out.println("这是一辆 奥迪SUV汽车");
    }
}

public class BMWSUVCar implements  SUVCar {
    @Override
    public void showInfo() {
        System.out.println("这宝马的SUV系列");
    }
}
public interface CarFactorys {
    // 生成不同型号的汽车 ,两条产品线
    MiniCar produceMiniCar();
    SUVCar produceSUVCar();
}

public class AudiCarFactory implements CarFactorys {
    @Override
    public MiniCar produceMiniCar() {
        return new AudiMiniCar();
    }

    @Override
    public SUVCar produceSUVCar() {
        return new AudiSUVCar();
    }
}

public class BMWCarFactory implements CarFactorys {
    @Override
    public MiniCar produceMiniCar() {
        return new BMWMiniCar();
    }

    @Override
    public SUVCar produceSUVCar() {
        return new BMWSUVCar();
    }
}
public static void main(String[] args) {
    BMWCarFactory bmwCarFactory = new BMWCarFactory();
    MiniCar miniCar = bmwCarFactory.produceMiniCar();
    miniCar.showInfo();
}

3.1 三个工厂模式区别:

1、对于简单工厂,用于生产同一结构中的任意产品,对于新增产品不适用。

2、对于工厂方法,在简单工厂的基础上,生产同一等级结构中笃定产品,可以支持新增产品。

3、抽象工厂,用于生产不同种类(品牌)的相同类型(迷你,SUV),对于新增品牌可以,不支持新增类型

四、单例模式(Singleton)

一步步分析:

1、属性

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

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

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

单例模式的实现:

1、私有的构造方法

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

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

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

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

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

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

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 在这里是需要的,为了保证线程可见性,可以重点查下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)

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

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

1、静态代理

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

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

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

/**
 * 接口
 * 
 * @author Mona
 *
 */
public interface UserDao {
	void save();
}
/**
 * 接口实现
 * 目标对象
 * 
 * @author Mona
 *
 */
public class UserDaoImpl implements UserDao{
    @Override
	public void save() {
		System.out.println("-------已保存数据--------");
		
	}
}
/**
 * 代理对象,静态代理
 * 
 * @author Mona
 *
 */
public class UserDaoProxy implements UserDao {
	//接收保存目标对象
	private UserDaoImpl target;
	public UserDaoProxy(UserDaoImpl target) {
		this.target = target;
	}
	
    @Override
	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();//执行的是代理的方法
	}
}

静态代理总结:

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("开启事务~");
                        // 执行目标对象方法
                        Object returnValue = method.invoke(target, args);
                        System.out.println("提交事务~");
                        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

  • JDK的动态代理有一个限制,就是使用动态代理的对象必须实现一个或多个接口。
    如果想代理没有实现接口的类,就可以使用CGLIB实现。
  • CGLIB是一个强大的高性能的代码生成包,它可以在运行期扩展Java类与实现Java接口。
    它广泛的被许多AOP的框架使用,例如Spring AOP,为他们提供方法的拦截
/**
 * 目标对象
 *
 * @author mona
 * @date 2021/11/18 15:09
 */
public class CglibDao {
    public void save() {
        System.out.println("保存数据");
    }
}
/**
 * 代理对象
 *
 * @author mona
 * @date 2021/11/18 15:09
 */
public class CglibProxyFactory implements MethodInterceptor {
    private Object target;

    public CglibProxyFactory(Object object) {
        this.target = object;
    }

    public Object getProxyInstance() {
        // 工具类
        Enhancer enhancer = new Enhancer();
        // 设置父类
        enhancer.setSuperclass(target.getClass());
        enhancer.setCallback(this);
        return enhancer.create();
    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("开启事务。。。sss");
        method.invoke(target, objects);
        System.out.println("关闭事务。。。eeee");
        return null;
    }
}
public class CglibTest {
    @Test
    public void cjlibTest() {
        // 目标对象
        CglibDao cjlibDao = new CglibDao();
        System.out.println(cjlibDao.getClass());
        // 代理对象
        CglibDao proxy = (CglibDao) new CglibProxyFactory(cjlibDao).getProxyInstance();
        System.out.println(proxy.getClass());
        proxy.save();

    }
}

结果:

class com.juc.demo.review.proxypag.CglibDao
class com.juc.demo.review.proxypag.CglibDao$$EnhancerByCGLIB$$f86aa22e
开启事务。。。sss
保存数据
关闭事务。。。eeee

动态代理对比:

  1. jdk动态代理必须实现InvocationHandler接口,通过反射代理方法,比较消耗系统性能,但可以减少代理类的数量,使用更灵活。
  2. cglib代理无需实现接口,通过生成类字节码实现代理,比反射稍快,不存在性能问题,但cglib会继承目标对象,需要重写方法,所以目标对象不能为final类。

六、装饰者模式(Decorator)

装饰器模式的应用场景:

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

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

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

public interface Human {
    void run();
}
public class Man implements Human{
    @Override
    public void run() {
        System.out.println("人可以跑");
    }
}
// 装饰的抽象类
public class AbstractDecorator implements Human {
    // 持有被装饰类的引用
    private Human human;

    public AbstractDecorator(Human human) {
        this.human = human;
    }

    // 调用被装饰类的方法
    @Override
    public void run() {
        human.run();
    }
}

public class ManDecorate extends AbstractDecorator {
    public ManDecorate(Human human) {
        // 调用父类的构造方法
        super(human);
    }

    // 装饰类增加的功能
    private void fly() {
        System.out.println("现在人可以飞了");
    }

    @Override
    public void run() {
        super.run();
        fly();
    }
}
//测试
public class DecorateClient {
    public static void main(String[] args) {
        Human human = new Man();
        ManDecorate manDecorate = new ManDecorate(human);
        manDecorate.run();
    }
}

装饰模式与代理模式的区别:

装饰模式:侧重给一个实现类动态添加功能,不会对实现类的方法进行过滤拦截

代理模式:侧重将一个实现类的功能,委托给代理类来处理,可以对实现类的方法进行过滤拦截

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

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

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

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

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

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

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

/**
 * 策略接口
 * 
 * @author Mona
 *
 */
public interface Strategy {
    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;
 
	public ContextStrategy(Strategy strate) {
		this.mStrategy = strate;
	}
 
	//封装后,策略方法
	public void onAnyWork() {
		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();
	}
}

优点:

1)良好的扩展性。增加一种策略,只要实现接口,写上具体逻辑就可以了。当旧策略不需要时,直接剔除就行。

2)良好的封装性。策略的入口封装在Context封装类中,客户端只要知道使用哪种策略就传哪种策略对象就可以了。

3)避免了像简单工厂模式这样的多重条件判断。

缺点:

1)客户端必须了解策略组的各个策略,并且决定使用哪一个策略,也就是各个策略需要暴露给客户端。

2)如果策略增多,策略类的数量就会增加。

7.1 代理模式、装饰器模式、策略模式的区别

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

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

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

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

public class Subject {
 
   private List<Observer> observers = new ArrayList<Observer>();
   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):实现父类中的抽象方法以完成算法中与特定子类相关的步骤。

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

/**
 * 模板设计模式
 * 	模板方法只负责定义第一步应该要做什么,第二步应该做什么,第三步应该做什么,至于怎么做,由子类来实现。
 * 
 * @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 就可以了。

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

十、适配器模式(Adapter)

适配器模式定义:

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

todo: 待补充

十一、建造者模式(Builder)

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

/**
 * 建造者模式
 * 
 * @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();
		
	}
}

参考文章:初探Java设计模式2:结构型模式(代理模式,适配器模式等)_黄小斜学Java-CSDN博客

java开发中的常用的设计模式_不远阑珊处的博客-CSDN博客_java常见设计模式

  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值