几种重要的设计模式

创建型模式(5种):

  • 工厂方法模式(Factory Method Pattern)
  • 抽象工厂模式(Abstract Factory Pattern)
  • 单例模式(Singleton Pattern)
  • 建造者模式(Builder Pattern)
  • 原型模式(Prototype Pattern)

结构性模式(7种)

  • 适配器模式(Adapter Pattern)
  • 装饰者模式(Decorator Pattern)
  • 代理模式(Proxy Pattern)
  • 外观模式(Facade Pattern)
  • 桥接模式(Bridge Pattern)
  • 组合模式(Composite Pattern)
  • 享元模式(Flyweight Pattern)

行为型模式(11种)

  • 策略模式(Strategy Pattern)
  • 模板方法模式(Template Method Pattern)
  • 观察者模式(Observer Pattern)
  • 迭代器模式(Iterator Pattern)
  • 责任链模式(Chain of Responsibility Pattern)
  • 命令模式(Command Pattern)
  • 备忘录模式(Memento Pattern)
  • 状态模式(State Pattern)
  • 访问者模式(Visitor Pattern)
  • 中介者模式(Mediator Pattern)
  • 解释器模式(Interpreter Pattern)

链接:https://www.jianshu.com/p/18fee11deac9

一、简单工厂模式(不是设计模式)

简单工厂模式属于类的创建型模式,又叫工厂方法模式,通过专门定义一个类来负责创建其他类的实例,被创建的实例通常都具有共同的父类。

  • 工厂角色:简单工厂模式的核心,负责实现创建所有实例的内部逻辑(比如用if语句:如果满足名字为apple的时候,返回一个apple实例对象),工厂类可以被外界直接调用,创建所需的产品对象(用反射机制获取类,生成实例)
public class Factory {
	/*
	 * 获得所有产品对象
	 */
	public static Fruit get(String type) throws InstantiationException, IllegalAccessException, ClassNotFoundException {
		
		Class fruit = Class.forName(type);  //动态加载名字为type的类
		return (Fruit) fruit.newInstance();	
	}
}

  • 抽象角色(接口):简单工厂模式所创建的所有对象的父类,负责描述所有实例所共有的公共接口
public interface Fruit {

	public void get();
}

  • 具体产品角色:简单工厂模式所创建的具体实例对象
public class Apple implements Fruit{

	public void get() {
		System.out.println("苹果");
	}
}

main文件:(这里的反射输入的类名需要为全类名,即为路径下的class名)

public class main {

	public static void main(String[] args) throws InstantiationException, IllegalAccessException, ClassNotFoundException {
		/*
		 * 通过工厂实例化一个apple对象
		 */
		Fruit apple1=Factory.get("factory.Apple");
		apple1.get();
	}
}

优点:工厂类包含必要的判断,可以根据外界给的信息,决定究竟该创建哪个具体的类的对象,用户使用时可以直接根据工厂类去创建所需要的实例,无需了解这些对象是如何创建的。
缺点:由于工厂类中集中了所有实例的创建逻辑,“高内聚”方面做得不好;当系统中的具体产品不断增多时,工厂类也要做相应修改,扩展性不太好。

二、工厂方法模式

工厂方法模式又称为多态工厂模式,定义一个创建产品对象的工厂接口将实际创建工作推迟到子类中,核心工厂类不再负责产品的创建,核心类成为了一个抽象工厂的角色,仅负责具体工厂子类必须实现的接口。这样可以使得不修改具体工厂角色的情况下引进新产品。

  • 抽象工厂角色:工厂方法模式的核心,任何工厂类都必须实现这个接口
public interface FruitFactory {

	public Fruit getFruit() ;
}
  • 具体工厂角色:具体工厂类是抽象工厂的实现,负责实例化产品对象(每多一个产品,都需要单独新建一个该产品的具体工厂类)
public class AppleFactory implements FruitFactory {

	public Fruit getFruit() {
		return new Apple();
	}
}
  • 抽象角色:工厂方法模式所创建的所有对象的父类,负责描述所有实例所共有的公共接口
public interface Fruit {

	public void get();
}
  • 具体产品角色:工厂方法模式所创建的具体实例对象
public class Apple implements Fruit{

	public void get() {
		System.out.println("苹果");
	}
}

main文件:(通过新建具体产品的工厂的对象,得到具体产品)

public class main {

	public static void main(String[] args) throws InstantiationException, IllegalAccessException, ClassNotFoundException {
	
		FruitFactory fruit=new AppleFactory();
		Fruit apple=fruit.getFruit();
		apple.get();
	}
}

优点:当系统扩展需要添加新的产品对象时仅仅需要添加一个具体对象以及一个具体工厂对象,原有工厂对象不需要进行任何修改,符合“开放-封闭”原则,而简单工厂模式在添加新产品对象后不得不修改工厂方法,扩展性不好。

应用场景:

  • 当不知道该使用对象的确切类型的时候
  • 当希望为库或者框架提供扩展其内部组件的方法时

优点:

  • 将具体产品和创建者解耦
  • 符合单一职责原则
  • 符合开闭原则
三、抽象工厂模式

抽象工厂(Creator)的接口包含了一系列相关的工厂方法(ProductA、ProductB)的接口(是由一系列工厂方法模式组合而成的)
在这里插入图片描述

  • 抽象工厂角色(Creator):抽象工厂的核心,包含对多个产品结构(ProductA、ProductB)的声明,任何工厂类都必须实现这个接口。
  • 具体工厂角色(ConcreteCreator1):具体工厂是抽象工厂的实现,负责实例化某个产品族中的产品对象(要同时实现ProductA和ProductB的实例化)
  • 抽象角色(ProductA):抽象模式所创建的所有对象的父类,负责描述所有实例所共有的公共接口。
  • 具体产品角色(ProductA1、ProductA2):抽象模式所创建的具体实例对象。

例如一个数据库的抽象工厂角色,需要有建立连接、发送指令两个产品,这两个产品就是抽象角色,不同的数据库都需要分别实现这两个产品的接口,实例化的数据库比如mysql就是一个具体工厂角色,里面包含的mysql建立连接、mysql发送指令两个方法都是具体产品角色。


public class AbstractFactory {

	public static void main(String[] args) {
		IDatabaseUtils iDatabaseUtils=new MysqlDatabaseUtils();
		IConnection connection=iDatabaseUtils.getConnection();
		connection.connect();
		ICommand command=iDatabaseUtils.getCommand();
		command.command();
		
		IDatabaseUtils iDatabaseUtils2=new OracleDatabaseUtils();
		IConnection connection2=iDatabaseUtils2.getConnection();
		connection2.connect();
		ICommand command2=iDatabaseUtils2.getCommand();
		command2.command();
	}

}

interface IConnection{
	void connect();
}

//针对不同的数据库实现连接
class MysqlConnection implements IConnection{
	@Override
	public void connect() {
		System.out.println("mysql connection");
		
	}
}

class OracleConnection implements IConnection{
	@Override
	public void connect() {
		System.out.println("oracle connection");
		
	}
}

//抽象角色
interface ICommand{
	void command();
}

//具体产品角色
class MysqlCommand implements ICommand{
	@Override
	public void command() {
		System.out.println("mysql command");
		
	}
}

class OracleCommand implements ICommand{
	@Override
	public void command() {
		System.out.println("oracle command");	
	}
}

//抽象工厂角色
interface IDatabaseUtils{
	IConnection getConnection();
	ICommand getCommand();
}

//具体工厂角色
class MysqlDatabaseUtils implements IDatabaseUtils{

	@Override
	public IConnection getConnection() {
		return new MysqlConnection();
	}

	@Override
	public ICommand getCommand() {
		return new MysqlCommand();
	}
}


class OracleDatabaseUtils implements IDatabaseUtils{

	@Override
	public IConnection getConnection() {
		return new OracleConnection();
	}

	@Override
	public ICommand getCommand() {
		return new OracleCommand();
	}
}

应用场景:
程序需要处理不同系列的相关产品,但是你不希望它依赖于这些产品的具体类时,使用抽象工厂

优点:
如果需要增加一个工厂,可以直接继承或实现抽象工厂类(里面包含的所有产品都一块实现),如果需要增加一个产品,则在抽象工厂里面新增加一个接口,其他的工厂都必须实现该产品的接口。

  • 从工厂得到的产品是彼此兼容的
  • 可以避免具体产品和客户端代码之间的紧密耦合
  • 符合单一职责原则
  • 符合开闭原则

抽象工厂模式和工厂模式的区别:
工厂方法模式::一个抽象产品类,可以派生出多个具体产品类。
抽象工厂模式::一个抽象工厂类,可以派生出多个具体工厂类。
在这里插入图片描述

四、工厂模式在项目中的应用–计算器实现

利用简单工厂模式:
(1)工厂角色:(根据不同的逻辑选择不同的产品实例)

public class OperationFactory {

	public static Operation getOperation(String oper) {
		if("+".equals(oper)) {
			return new AddOperation();
		}else {
			return null;
		}
	}
}

(2)抽象角色:

public abstract class Operation {

	private double num1;
	private double num2;
	
	public double getNum1() {
		return num1;
	}
	public void setNum1(double num1) {
		this.num1=num1;
	}
	public double getNum2() {
		return num2;
	}
	public void setNum2(double num2) {
		this.num2=num2;
	}
	
	public abstract double getResult() ;
	
}

(3)具体产品角色:

public class AddOperation extends Operation{

	@Override
	public double getResult() {
		double result=this.getNum1()+this.getNum2();
		return result;
	}
}

(4)main:

import java.util.Scanner;

public class Main {

	public static void main(String[] args) {

		//1、接受控制台输入
		System.out.println("---计算器---");
		System.out.println("输入第一个操作数:");
		Scanner scanner =new Scanner(System.in);
		String strNum1=scanner.nextLine();
		System.out.println("输入运算符:");
		String oper=scanner.nextLine();
		System.out.println("输入第二个操作数:");
		String strNum2=scanner.nextLine();
		double result=0;
		double Num1=Double.parseDouble(strNum1);
		double Num2=Double.parseDouble(strNum2);
		//2、进行运算
		Operation operation=OperationFactory.getOperation(oper);
		operation.setNum1(Num1);
		operation.setNum1(Num2);
		result=operation.getResult();
		System.out.println(result);
	}
}
五、单例模式

单例模式是一种对象创建型模式,使用单例模式,可以保证一个类只生成唯一的实例对象。
(1)饿汉式
类加载的时候就会产生类的实例对象,不管有没有调用getPerson方法。

public class Person {
	
	public static final Person person=new Person();
	private Person() {
		
	}
	public static Person getPerson() {
		return  person;
	}
}

(2)懒汉式
①静态内部类的实现方式
只有显示调用了getInstance方法时,才会显示装在SingletonHolder类,从而实例化类的对象(只有第一次使用这个单例的实例时才会加载,同时不会出现线程安全问题)

public class Singleton {

	private static class SingletonHolder{
		private static final Singleton instance=new Singleton();
	}
	private Singleton() {
		
	};
	
	public static Singleton getInstance() {
		return SingletonHolder.instance;
	}
}

②双重检查加锁版本
在多线程里面为了保证线程安全只创建一个实例,必须经过两次检查实例是否被创建,且用synchronized代码块保证线程安全。

public class Singleton {

	private volatile static  Singleton instance;
	private Singleton() {
		
	};
	
	public static Singleton getInstance() {
		if(instance==null) {
			synchronized (Singleton.class) {
				if(instance==null) {
					instance=new Singleton();
				}
			}
		}
		return instance;
	}
}
六、观察者模式

观察者模式是行为模式之一,作用是当被观察者对象的状态发生变化时,能够自动通知其他的观察者对象,自动刷新对象状态。
提供给关联对象一种同步通信的手段,使某个对象与依赖它的其他对象之间保持状态同步。
(1)被观察者-----(注册、清除注册、状态变化提醒)
当被观察的对象状态发生变化时,需要通知队列中所有观察者对象,被观察者需要维持(添加、删除、通知)一个观察者对象的队列列表。

/*
 * 抽象被观察者接口
 */

public interface Subject {
	public void registerObserver(observer o);	// 观察者想要获取数据源时调用此方法注册
	public void removeObserver(observer o);		//观察者不再需要数据源数据时,退出注册
	public void notifyObservers();				//数据源向注册在案的所有观察者发送数据
}

(2)被观察者的具体实现
需要维护注册队列、状态更新后的提醒方法

import java.util.ArrayList;
import java.util.List;
 
public class WeatherData implements Subject{
	private List<observer> observers=new ArrayList<>();
	private double temperature;
	
	/*实现Subject接口的注册方法*/
	public void registerObserver(observer o) {
		observers.add(o);	
	}
 
	/*实现Subject接口的取消注册方法*/
	public void removeObserver(observer o) {
		int i = observers.indexOf(o);
		if(i>=0) {
			observers.remove(i);
		}
	}
 
	/*实现Subject接口的notifyObservers(),用于向所有注册的观察者发送数据*/
	public void notifyObservers() {
		for(int i=0;i<observers.size();i++) {
			observer observer = (observer)observers.get(i);
			observer.update(temperature);
		}
	}
	
	
	public void measurementsChanged() {
		notifyObservers();
	}
	
	/*向获取数据的设备提供接口,当气象观察站获得新数据,会调用此接口向WeatherData写数据,以便WeatherData向观察者发送数据*/
	public void setMeasurements(double temp) {
		this.temperature = temp;
		measurementsChanged();		//表示有了新数据
	}

}

(3)观察者-----(被观察者的状态变化后的更新方法)
接口或者抽象类,当被观察者的对象发生变化时,观察者将通过一个callback函数得到通知。

/*
 * 抽象观察者接口
 */

public interface observer {
	public void update(double temp);//观察者当接收到来自数据源的信息时,调用此方法更新自身数据
}

(4)观察者的具体实现-----(向被观察者注册对象)
得到通知后将完成一些具体的业务逻辑处理

public class ConditionDisplay implements observer{
	private double temperature;

	private String name;
	private Subject weatherdata;	//	观察者也要维护一个中介(WeatherData)作为数据源
	
	/*当观察者初始化时,需要指定数据源和观察者的名字*/
	public ConditionDisplay(Subject weatherdata,String name) {
		this.name = name;
		this.weatherdata = weatherdata;
		weatherdata.registerObserver(this);		//向数据源注册,代表需要从数据源获取数据
	}
	
	/*把数据打印给用户*/
	public void display() {
		System.out.println("观察者"+name+"的数据:");
		System.out.println("temperature:"+temperature);
		System.out.println();
	}
 
	/*当从数据源(WeatherData)获得数据后,用新数据更新自身数据*/
	public void update(double temp) {
		this.temperature = temp;
		display();					//信息更新完后,自动打印
	}
	
}
七、代理模式

代理模式,提供了对目标对象另外的访问方式;即通过代理对象访问目标对象.这样做的好处是:可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能.
在这里插入图片描述
这里使用到编程中的一个思想:不要随意去修改别人已经写好的代码或者方法,如果需改修改,可以通过代理的方式来扩展该方法。
代理模式的关键点是:代理对象与目标对象.代理对象是对目标对象的扩展,并会调用目标对象

1、静态代理

(1)subject(抽象主题角色)
真是主题与代理主题的共同接口

public interface Subject {

	public void sailBook();
}

(2)(RealSubject)真实主题角色(目标对象)
定义了代理角色所代表的真是对象

public class RealSubject implements Subject{
	/*目标对象
	 */
	@Override
	public void sailBook() {
		System.out.println("卖书");	
	}
}

(3)代理主题角色(代理对象)
含有对真实主题角色的引用,代理角色通常在将客户端调用传递给真实主题对象之前或者之后执行操作。

public class Proxy implements Subject{
	private RealSubject realsubject;

	@Override
	public void sailBook() {
		if(realsubject==null) {
			realsubject=new RealSubject();
		}
		dazhe();
		this.realsubject.sailBook();
		
	}
	//代理对象所执行的其他操作
	public void dazhe() {
		System.out.println("打折");
	}
}

(4)main
客户端,直接操作代理对象即可

public class Main {	
	 /* 用户
	 */
	public static void main(String[] args) {
		Proxy proxy=new Proxy();
		proxy.sailBook();
	}
}
2、动态代理

利用jdk中的InvocationHandler接口

  • 实现nvocationHandler
  • 重写invoke方法
  • 得到代理类实例Proxy.newProxyInstance()

(1)subject(抽象主题角色)

public interface Subject {

	public void sailBook();
}

(2)(RealSubject)真实主题角色(目标对象)

public class RealSubject implements Subject{
	/*目标对象
	 */
	@Override
	public void sailBook() {
		System.out.println("卖书");	
	}
}

(3)代理对象需要执行的程序

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

public class MyHandler implements InvocationHandler {

	RealSubject realsubject;
	public void setRealsubject(RealSubject realsubject) {
		this.realsubject = realsubject;
	}
	
	//在代理实例上处理方法并调用结果
	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		// TODO Auto-generated method stub
		Object result;
		dazhe();
		result=method.invoke(realsubject, args);
		return result;
	}
	
	public void dazhe() {
		System.out.println("打折");
	}
}

(4)main
利用反射中的Proxy.newProxyInstance(代理类的类加载器,代理类要实现的接口,指派方法调用的调用处理程序)方法,返回一个指定接口的代理类实例proxy,该接口可以将方法调用(proxy.sailBook())指派到指定的调用处理程序(handle)

import java.lang.reflect.Proxy;

public class Main {
	public static void main(String[] args) {
		RealSubject realsubject=new RealSubject();
		MyHandler handle=new MyHandler();
		handle.setRealsubject(realsubject);
		//输入参数:代理类的类加载器、代理类要实现的接口、指派方法调用的调用处理程序
		Subject proxy=(Subject)Proxy.newProxyInstance(RealSubject.class.getClassLoader(), realsubject.getClass().getInterfaces(), handle);
		proxy.sailBook();
	}
}

当在main文件中调用sailBook()方法时,在MyHandler中被invoke方法拦截,并执行该方法.

八、装饰模式

装饰模式又叫包装模式,通过一种对客户端透明的方式来扩展对象的功能,可以实现继承关系。
在这里插入图片描述

  • Component,抽象构件
      Component是一个接口或者抽象类,是定义我们最核心的对象,也可以说是最原始的对象,比如定义了一个相机

  • ConcreteComponent,具体构件,或者基础构件
      ConcreteComponent是最核心、最原始、最基本的接口或抽象类Component的实现,可以单独用,也可将其进行装饰,比如定义了相机可以有拍照功能。

  • Decorator,装饰角色
      一般是一个抽象类,继承自或实现Component,在它的属性里面有一个变量指向Component抽象构件,是装饰器最关键的地方。

  • ConcreteDecorator,具体装饰角色
      ConcreteDecoratorA和ConcreteDecoratorB是两个具体的装饰类,它们可以把基础构件装饰成新的东西,比如相机有美颜效果。每个具体的装饰角色都能实现抽象构件的具体实现方法(拍照)。

public class Main {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Component component=new ConcreteComponent();
		Component component1=new ConcreteDecorator(component);
		component1.operation();
		Component component2=new ConcreteDecorator2(component);
		component2.operation();
	}
}

//抽象构件
interface Component{
	void operation();
}

//具体构件
class ConcreteComponent implements Component{
	@Override
	public void operation() {
		System.out.println("拍照");
			
	}
}

//装饰角色
abstract class Decorator implements Component{
	Component component;
	public Decorator(Component component) {
		this.component=component;
	}
}

//具体装饰角色
class ConcreteDecorator extends Decorator{

	public ConcreteDecorator(Component component) {
		super(component);
		// TODO Auto-generated constructor stub
	}

	@Override
	public void operation() {
		component.operation();
		System.out.println("添加美颜效果");
	}
}

//具体装饰角色
class ConcreteDecorator2 extends Decorator{

	public ConcreteDecorator2(Component component) {
		super(component);
		// TODO Auto-generated constructor stub
	}

	@Override
	public void operation() {
		component.operation();
		System.out.println("添加滤镜效果");
	}	
}

输出:

拍照
添加美颜效果
拍照
添加滤镜效果

优点:

  • 不改变原有对象的情况下给一个对象扩展功能
  • 使用不同的组合可以实现不同的功能,还可以一个装饰器嵌套另一个装饰器,因为指向的都是Component
  • 符合开闭原则(对修改关闭,对拓展开放)
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值