设计模式

142 阅读23分钟

设计模式之创建篇

1、单例模式

进化过程


public Class Sun() {
	public volatitle static Sun sun;
	public static Sun getInstance () {
		if(sun == null) { // 防止重复创建
					sun = new Sun();
		}
		return sun;
	}
}

public Class Sun() {
	public volatitle static Sun sun;
	public static Sun getInstance () {
		Sychronize(Sun.class) {//加锁保证线程安全
			if(sun == null) { // 防止重复创建
						sun = new Sun();
			}
		}
		return sun;
	}
}

public Class Sun() {
	public volatitle static Sun sun;
	public static Sun getInstance () {
		if(sun == null) { //减少使用锁的次数
			Sychronize(Sun.class) {//加锁保证线程安全
				if(sun == null) { // 一 防止重复创建
					sun = new Sun();
				}
			}	
		}
		return sun;
	}
}

原型模式

原型模式理解为用对象创建对象而不是用类创建对象 打印机 打印出来 复印机 再复印 复印的过程就是原型模式的使用过程

究其本质,克隆操作时Java虚拟机会进行内存操作,直接拷贝原型对象数据流生成新的副本对象,绝不会拖泥带水地触发一些多余的复杂操作(如类加载、实例化、初始化等),所以其效率远远高于“new”关键字所触发的实例华操作

2、工厂方法模式

简单工厂随着发展越来越不简单 定义一个工厂接口

public interface Factory{
 	Enemy create(int screentWidth);
 }
public class TankFactory implements Factory{
	public Enemy create(int screentWidth) {
		return new Tank(random.nextInt(screentWidth));
	}
}

3、抽象工厂模式

抽象工厂模式可以理解为工厂方法模式的高度集群化升级版

产品规划

public abstract class Unit{
	protected int attack; // 攻击
	protected int defence;
	protected int health;
	protected int x;坐标
	protected int y;
	public Unit(int attack,int defence,int health,int x,int y){
		this.attack = attack;
		this.defence = defence;
		this.health = health;
		this.x = x;
		this.y = y;

	}
	public abstract void show();
	public abstract void attack();
}

public abstract class LowClassUnit extends Unit{
	public LowClassUnit(int x, int y) {
		super(5, 2, 35, x,y);
	}
}

public abstract class MidClassUnit extends Unit{
	public MidClassUnit(int x, int y) {
		super(10, 8, 80, x,y);
	}
}

public abstract class HightClassUnit extends Unit{
	public HightClassUnit(int x, int y) {
		super(25, 30, 300, x,y);
	}
}



public class Marine extends LowClassUnit {
	public Marine(int x, int y){
		super(x,y);
	}
	public void show() {
		System.out.println("士兵出现在坐标"+x+"-"+y);
	}
	public void attach() {
		System.out.println("士兵攻击力"+attack);
	}
}

public class Tank extends MidClassUnit() {
	public Tank(int x, int y) {
		super(x,y);
	}
	public void show () {
		System.out.println("坦克出现在坐标"+x+"-"+y);

	}
	public void attach() {
		System.out.println("坦克攻击力"+attack);
	}
}

public class Battleship extends HightClassUnit{
	public Battleship(int x, int y ){
		this.x = x;
		this.y = y;
	}
	public void show () {
		System.out.println("战舰出现在坐标"+x+"-"+y);

	}
	public void attach() {
		System.out.println("战舰攻击力"+attack);
	}
}

public class Roach extends LowClassUnit {
	public Roach(int x, int y){
		super(x,y);
	}
	public void show() {
		System.out.println("蟑螂兵出现在坐标"+x+"-"+y);
	}
	public void attach() {
		System.out.println("蟑螂兵攻击力"+attack);
	}
}

public class Poison extends MidClassUnit() {
	public Poison(int x, int y) {
		super(x,y);
	}
	public void show () {
		System.out.println("毒液出现在坐标"+x+"-"+y);

	}
	public void attach() {
		System.out.println("毒液攻击力"+attack);
	}
}

public class Mammoth extends HightClassUnit{
	public Mammoth(int x, int y ){
		this.x = x;
		this.y = y;
	}
	public void show () {
		System.out.println("猛犸巨兽出现在坐标"+x+"-"+y);

	}
	public void attach() {
		System.out.println("猛犸巨兽攻击力"+attack);
	}
}

生产线规划


public interface AbstractFactory{
	LowClassUnit createLowClass();
	MidClassUnit createMidClass();
	HightClassUnit createHighClass();

}
// 人类兵工厂
public class HumanFactory implements AbstractFactory{
	private int x; //工厂横坐标 
	private int y;
	public HumanFactory(int x, int y ){
		this.x = x;
		this.y = y;
	}
	public LowClassUnit createLowClass(){
		LowClassUnit Unit = new Marine(x, y);
		System.out.println("制造海军陆战队队员成功");
		return unit;
	}
	public MidClassUnit createMidClass(){
		MidClassUnit Unit = new Tank(x, y);
		System.out.println("制造变型坦克成功");
		return unit;
	}
	public HightClassUnit createHighClass(){
		HightClassUnit unit = new Battleship(x,y);
		ystem.out.println("制造巨型战舰成功");
		return unit;
	}
}

//外星母巢
public class AlienFactory implements AbstractFactory{
	private int x; //工厂横坐标 
	private int y;
	public AlienFactory(int x, int y ){
		this.x = x;
		this.y = y;
	}
	public LowClassUnit createLowClass(){
		LowClassUnit Unit = new Roach(x, y);
		System.out.println("制造蟑螂兵成功");
		return unit;
	}
	public MidClassUnit createMidClass(){
		MidClassUnit Unit = new Poison(x, y);
		System.out.println("制造毒液成功");
		return unit;
	}
	public HightClassUnit createHighClass(){
		HightClassUnit unit = new Mammoth(x,y);
		ystem.out.println("制造猛犸巨兽成功");
		return unit;
	}
}

客户端

public static void main(String[] args) {
	System.out.println("游戏开始");
	System.out.println("建造人类兵工厂");
	AbstractFactory factory = new HumanFactory(10,50);
	Unit marine = factory.createLowClass();
	marine.show();
	Unit tank = factory.createMidClass();
	tank.show();
	System.out.println("建造建造外星工厂");
	AbstractFactory factory = new AlienFactory(30,54);
	Unit roach = factory.createLowClass();
	roach,show();

	System.out.println("两族大战开始");
	marine.attack();
	roach.attack();

}

总结: AbstractFactory 抽象工厂可以是接口也可以是抽象类 抽象工厂模式一定是基于产品的族系划分来布局的,其产品系一定是相对固定的,故可以抽象工厂来确定工业制造标准

4、建造者模式

建造者模式主要用于对复杂对象的构、初始化,他可以将多个简单的组建对象按顺序一步步组装起来,最终构成一个复杂的成品对象。 与工厂模式不同的是,建造者模式主要目的是将繁琐的构建过程从不同对象抽象出来,使其脱离并独立于产品类工厂类,最终实现使用 同一套标准制造工序产出不同产品。

建筑类

public class Building {
	private List<String> buildingComponents = new Arraylist();
	//地基
	public void setBasement(String basement) {
		this.buildingComponents.add(basement);
	}
	//墙体
	public void setWall(String wall) {
		this.buildingComponents.add(wall);
	}
	public void setRoof(String roof) {
		this.buildingComponents.add(roof);
	}
	public String toString() {
       String buildingStr = "";
       for (int i = buildingComponents.size() - 1; i >= 0; i--) {
          buildingStr += buildingComponents.get(i);
       }
         return buildingStr;
    } 
 
}

建筑施工方

public interface Builder{
	public void buildBasement();
	public void buildWall();
	public void buildRoof();
	public Building getBuilding();
}
//别墅
public HouseBuilder implements Builder{
	private Building house;
	public HouseBuilder() {
		house = new Building();
	}

	//地基
	public void buildBasement() {
		System.out.println("挖土方,部署管道、线缆,水泥加固,搭建围墙、花园");
		house.setBasement("=============\n")
	}
	public void buildWall() {
		System.out.println("搭建木质框架,石膏板封墙并粉饰内外墙");
		house.setWall("|田|田|田|田|田|\n");
	}
	public void buildRoof() {
		System.out.println("造木质屋顶、阁楼、安装烟囱");
		house.setRoof("/-----------\\\n");
	}
}

工程总监


public class Director() {
	private Builder builder;
	public Director(Builder builder) {
		this.builder = builder;
	}
	public void setBuilder(Builder builder) {
		this.builder = builder;
	}
	public Building direct() {
		System.out.println("==工程项目启动==");
		//第一步打地基
		builder.buildBasement();
		//第二步 构造墙体
		builder.buildWall();
		//第三步 封顶
		builder.buildRoof()
		System.out.println("==项目竣工==");
		return builder.getBuilding();

	}
}

项目实施


public static void main() {
	Director director = new Director(new HouseBuilder);
	System.out.println(director.direct());
	//替换施工队
	director.setBuilder(new AartmentBuilder());
	System.out.println(director.direct());

}

复杂对象的构建显然需要专业的建造团队,建造标准的确立让产品趋向多样化,其建造工艺可以交给多位建造者去各显其长,而建造工序则交由工程总监去全局把控,把“变”与“不变”分开,使“工艺多样化”“工序标准化”,最终实现通过相同的构建过程生产出不同产品,这也是建造者模式要达成的目标

设计模式之结构篇

5、门面模式

可能是最简单的结构型设计模式。也叫外观模式 直接理解为 在实际开发的 service 层会整合多个dao层的数据操作,然后service 层只需要对controller 提供一个方法入口。

6、组合模式

设计模式中的组合模式(Composite Pattern)是一种结构型设计模式,主要用于将对象组合成树状层次结构,以表示“整体-部分”的关系,并使得客户端对单个对象和组合对象具有一致的访问方式。 在开发中,组合模式的应用案例非常广泛。以下是一些具体的使用案例,用于说明组合模式在实际开发中的应用。

文件系统 组合模式可以很好地应用于文件系统的实现。在文件系统中,目录(文件夹)可以包含其他目录和文件,而文件则不包含其他对象。这种结构正是组合模式的典型应用。通过组合模式,我们可以一致地处理文件和目录,无论是进行遍历、搜索还是删除操作。

图形界面 在图形用户界面(GUI)设计中,组合模式也经常被使用。例如,一个窗口可以包含多个按钮、文本框和面板等控件,而面板本身也可以包含其他控件。通过使用组合模式,我们可以方便地管理和操作这些控件,无论是单独处理还是整体处理。

XML解析 在解析XML文档时,组合模式也非常有用。XML文档通常具有嵌套结构,如元素可以包含其他元素或文本内容。通过组合模式,我们可以将XML元素和文本内容视为对象,并构建出与XML文档结构相对应的树形结构,从而方便地进

7、装饰器模式

装饰器能够在运行时动态的为对象增加功能,功能能上类似于“继承”,但是继承是在编译时静态的地对原始类的继承完成,而前者能在程序运行时对原始对象静态的“包装”完成。 下面以化妆为例,用代码完成装饰模式模拟

//展示类
public interface Showable{
	public void show();
}

public class Girl implements Showable{
	@Override
	public void show() {
		System.out.print("女生素颜");
	}
}

public abstract class Decorator implements Showable {

	protected Showable showable;

	public Decorator(Showable showable) {
		this.showable = showable; 
	}
	@Override
	public void show() {
		showable.show();
	}
}

//粉底装饰器
public class FoundationMakeup extends Decorator{ 
	public FoundationMakeup() {
		super();
	}
	@Override
	public void show() {
		System.out.print("打底[");
		showable.show();
		System.out.print("]");
	}
}

//口红装饰器
public class Lipstick extends Decorator{
	public Lipstick(){
		super();
	}
	@Override
	public void show() {
		System.out.print("涂口红【");
		showable.show();
		System.out.print("]");
	}
}

public class Client{
	public static void main(String[] args) {
		//口红包裹粉底 粉底再包裹女生
		showable madeupGirl = new Lipstick(new FoundationMakeup(new Girl()));
		madeupGirl.show();
	}
}

装饰器如俄罗斯套娃一般,而装饰器模式在java 的工具包有大量应用,例如java 的io包当中

File file = new File("文件.zip");
ZipInputStream ZipInputStream = new ZipInputStream(
	new BufferInputStream(
		new FileInputStream(file)));

创建一个抽象装饰类而不是让装饰器子类直接继承接口,是为了减少直接实现接口带来代码冗余,而继承抽象接口可以决定重写哪个方法

8、适配器模式

跨语言交流必须找一个会两种语言的人充当翻译,将翻译这个角色定义为适配器。 下面以插头插孔为例编写适配器程序


//三孔
public interface TriplePin{
	// l 零线 n 火线 e 地线
	public void electrify(int l, int n, int e);
}
//两孔
public interface DualPin{
	public void electrify(int n, int e);
}

对象适配器(通用适配器)

public class TV implements DualPin{
	public void electrify(int l, int n){
		System.out.print("火线通电:"+l+",零线通电:"+n);
		System.out.print("电视开机");
	}
}

public class Adapter implements TriplePin() {
	private DualPin dualPin;

	//创建适配器的时候需要把两插设备接入进来
	public Adapter(DualPin dualPin){
		this.dualPin = dualPin;
	}

	@Override
	public void electrify(int l, int n, int e){
		dualPin.electrify(l,n);
	}
}

public class Client{
	public static void main(String[] args) {
		DualPin dualPin = new TV();

		TriplePin triplePin = new Adapter(dualPin);
		triplePin.electrify(1,0,-1);
	}
}

适配器引入的是两插的类型对象,可以是洗衣机电视等等。

类适配器(专属适配器)

public class TVAdapter extends TV implements TriplePin{

	//必须实现接口方法
	@Override
	public void electrify(int l , int n , int e){
		super.electrify(l,n);
	}
}
public class Client{
	TriplePin TVAdapter = new TVAdapter();
	//此处调用的是三插通电标准
	TVAdapter.electrify(1,0,-1);
}

类适配器与电视节的继承关系让他固化为一种专属适配器,这就造成了继承耦合,需要别的适配器就变得无能为力。

对象适配器模式与类适配器模式基本相同,二者的区别在于前者的Adaptee(被适配者)以接口形式出现并被Adapter(适配器)引用,而后者则以父类的角色出现并被Adapter(适配器)继承,所以前者更加灵活,后者则更为简便。其实不管何种模式,从本质上看适配器至少都应该具备模块两侧的接口特性,如此才能承上启下,促成双方的顺利对接与互动

9、享元模式

享元”则是共享元件的意思。享元模式的英文flyweight是轻量级的意思,这就意味着享元模式能使程序变得更加轻量化。当系统存在大量的对象,并且这些对象又具有相同的内部状态时,我们就可以用享元模式共享相同的元件对象,以避免对象泛滥造成资源浪费。

可能在平时开发中我们觉得没有用过享元模式,其实享元模式就是避免重复创建对象,然后创建一个对象通过对象传参不同来获得不同的结果

10、代理模式

静态代理

以上网为例,代码模拟如下

//定义一个通用上网接口.上网设备需要实现该接口
public interface Internet{
	void httpAccess(String url);
}

//调制解调器类

public class Modem implements Internet{
	public Modem(String password) throws Exception{
		if(!"123456".equals(password)) {
			throw new Exception("拨号失败请重试");
		}
		System.out.print("拨号上网成功");
	}

	@Override
	public void httpAccess(String url) {
		System.out.print("正在访问" + url);
	}
}

//路由器代理类

public class RouterProxy implements Internet{
	//被代理对象
	private Internet modem;
	//黑名单
	private List<String> list = Arrays.asList("电影","游戏","小说");
	public RouterProxy() throw Exception {
		//实例画被代理类
		this.modem = new Modem("123456");
	}

	@Override
	public void httpAccess(String url) {
		for (String keyword : list) {
			if(url.contains(keyword)) {
				System.out.print("禁止访问"  + url);
				return;
			}
		}
		//发送请求指 “猫”开始上网
		modem.httpAccess(url);
	}
}

public class Client{
	public static void main(String[] args) {
		Internet proxy = new RouterProxy();
		proxy.httpAccess("http://www.dianying.com");
		proxy.httpAccess("http://www.youxi.com");
	}
}

静态代理模式和装饰模式很像 RouerProxy 作为一个装饰器类对Modem,但是他们也有目的上的区别

装饰者模式是为了增强目标类,而代理模式是为了保护隐藏目标对象(目标对象在代理类中创建),让客户只能访问代理对象

装饰着模式中的目标类引用是通过代参构造器传入的;静态代理模式的目标类引用,一般是在代理类中直接创建的,目的就是为了隐藏目标

装饰器模式使用多个装饰器可以形成一个装饰器链,而静态代理会对目标对象进行增强,而需要增强哪些功能,一次在静态代理类中完成,没有增强链的概念

动态代理

上面静态代理使用了猫的代理对象来实现黑名单处理对外网访问的,同样局域网内部设备增多需要通过连接交换机,交换机再连接路由器对外访问,这是计算机网络相关技术。 现在我们将视角切换到局域网,定一个局域网访问接口

//局域网访问接口
public interface Intranet{
	public void fileAccess(String path);
}
//交换机 实现局域网访问接口
public class Switch implements Intranet{
	@Override
	public void fileAccess(String path){
		System.out.print("访问内网:" + path);

	}
}

加上之前的路由器代理 这里的交换机代理 都需要加黑名单过滤,如果每个设备都要写一个代理类势必很麻烦


public class BlackListFilter implements InvocationHandler {

	private List<String> blackList = Arrays.asList("电影","游戏","小说");

	//被代理的真实对象 如 猫 交换机等
	private Object origin;

	public BlackListFilter(Object origin) {
		this.origin = origin;
		System.out.print("开启黑名单过滤功能");
	}

	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		String arg = args[0].toString;
		for(String keyword : blackList) {
			if(arg.contains(keyword)) {
				System.out.print("禁止访问":+ arg);
				return null;
			}
		}
		System.out.print("校验通过");
		return method.invoke(origin, arg);
	}
}
pubic class Client {
	public static void main(String[] args) {
		Intranet intranet = (Intranet)Proxy.newProxyInstance(
			Switch.class.getClassLoader(),
			System.class.getInterfaces(),
			new BlackListFilter(new Switch())//传入要代理的对象
		);
		intranet.fileAccess("“\\\\192.68.1.2\\共享\\电影\\IronHuman.mp4");
		Internet internet = (Internet)Proxy.newProxyInstance(
			Modem.class.getClassLoader,
			Modem.class.getInterfaces,
			new BlackListFilter(new Modem())
		);
		internet.httpAccess("http://游戏.com");

	} 
}

11、桥接模式

桥接模式(Bridge)能将抽象与实现分离,使二者可以各自单独变化而不受对方约束,使用时再将它们组合起来,就像架设桥梁一样连接它们的功能,如此降低了抽象与实现这两个可变维度的耦合度,以保证系统的可扩展性 此材料可能受版权保护。

以画一幅抽象画为例

public abstract class Pen{
	public abstract void getColor();
	public void draw() {
		getColor();
		//形状是固定的
		System.out.print("▶");
	}
}

public BlackPen extends Pen{
	public void getColor(){
		System.out.print("黑");
	}
}

public class Client{
	Pen blackPen = new BlackPen();
	blackPen.draw();
	//会输出‘黑▶’
}

感觉和模板方法很相像是不是

接下来如何实现对形状的抽离

//尺子高层接口
public interface Ruller{
	public void regularize();
}

//方形尺子
public class SquareRuler implements Ruler{
	@Override
	public void regularize{
		System.out.print("▪");
	}
}

//三角形尺子
public class TriangleRuler implements Ruler{
	@Override
	public void regularize() {
		System.out.print("▶");
	}
}

public abstract class Pen{
	//尺子的引用
	protected Ruler ruler;
	public Pen(Ruler ruler) {
		this.ruler = ruler;
	}
	public abstract void draw();
}

//黑笔
public class BlackPen extends Pen{
	public BlackPen(Ruler ruler){
		super(ruler);
	}
	public void draw() {
		System.out.print("黑");
		ruler.regularize();
	}
}

//白笔

public class WhtitenPen extends Pen{
	public WhtitenPen(Ruller ruler){
		super(ruler);

	}
	public void draw() {
		System.out.print("白");
		ruler.regularize()
	}
}

public class Client{
	public static void main(String[] args) {
		new WhitePen(new CircleRuler()).draw();
		new BlackPen(new SquareRuler()).draw();
 
	}
}

通过例程我们可以看到,桥接模式将原本对形状的继承关系改为聚合(组合)关系,使形状实现从颜色中分离出来,最终完成多类组件维度上的自由扩展与拼装,使形与色的自由搭配成为可能。 桥接模式不是通过继承而是通过组合的方式来建立两个类之间的联系。 桥接模式的主要应用场景包括:

  • 当一个类内部具备两种或多种变化维度时,桥接模式可以解耦这些变化的维度,使高层代码架构稳定。
  • 当一个系统不希望使用继承或因为多层次继承导致系统类的个数急剧增加时,桥接模式可以作为一种替代方案。
  • 当一个系统需要在构件的抽象化角色和具体化角色之间增加更多的灵活性时,桥接模式也可以派上用场。

行为篇

12、模板方法

//哺乳动物类
public abstract class Mammal {
	public abstract void move();
	public abstract void eat();
	public final void live() {
		move();
		eat();
	}
}

//鲸鱼
public class Whale extends Mammal {
	@Override
	public void move() {
		System.out.print("鲸鱼在水里游泳");

	}
	@Override
	public void eat() {
		System.out.print("捕鱼吃");
	}
}
//人类
public class Human extends Mammal{

	@Override
	public void move() {
		System.out.print("人类在路上开车");
	}
	@Override
	public void eat() {
		System.out.print("去公司挣钱吃饭");
	}
}

public class Client{
	Mammal mammal = new Whale();
	mammal.live();
	mammal = new Human();
	mamman.live();
}

对于基类(Mammal)的步骤方法 move() eat()方法不是必须是抽象方法,完全可以用实体方法实现一些通用动作,如果子类需要个性化就对其重写变更,不需要就直接继承。

13、迭代器模式

迭代,在程序中特指对集合中各个元素逐个取用的行为。

14、责任链模式

代码模拟审批流程

//审批者抽象类
public abstract class Approver{
	//审批人的姓名
	protected String name;

	public Approver(String name) {
		this.name = name;
	}

	//下一个审批人
	protected Approver nextApprover;

	protected Approver setNextApprover(Approver nextApprover) {
		this.nextApprover = nextApprover;
		//返回下一个审批人支持链式审批
		return this.nextApprover;
	}

	//审批抽象方法
	public abstract void approve(int amount);
}

//员工类
public class Stuff extends Approver{

	public Stuff(String name){
		super(name);
	}

	// 子类只需要重写父类的审批方法
	@Override
	public void approve(int amount) {
		if(amount <=1000) {
			System.out.print("审批通过,专员【"+name+"】");
		} else {
			System.out.print("无权审批,升级处理。专员【"+name+"】");
			this.nextApprover.approve(amount);
		}
	}
}

//经理类
public class Manager extends Approver{

	public Manager(String name){
		super(name);
	}

	@Override
	public void approve(int amount) {
		if (amount <= 5000) {
			System.out.print("审批通过。经理【"+name+"】");
		} else {
			System.out.print("审批通过。经理【"+name+"】");
			this.nextApprover.approve(amount);
		}
	}

}

public class CFO extends Approver {
	public CFO(String name) {
		super(name);
	}

	@Override
	public void approve(int amount){
		if (amount <= 10000) {
			System.out.print("审批通过。财务总监【"+name+"】");
		} else {
			System.out.print("驳回申请,财务总监【"+name+"】");
		}
	}
}

public class Client {
	Approver staff = new Staff("员工小张");
	staff.setNextApprover(new Manager("王经理")).setNextApprover(new CFO("李总"));

	staff.approve(500);
	staff.approve(2000);
	staff.approve(20000);	
}

15、策略模式

策略是一种替换,如果要替换那就需要接口,因为具体的动作交给实现类,以此来实现策略替换

public class Strategy{

	public int calculate(int a, int b);

}

public class Subtraction implements Strategy{
	@Override
	public int calculate(int a, int b) {
		return a - b;
	}
}

public class Addition implements Strategy{
	@Override
	public int calculate(int a, int b) {
		return a + b;
	}
}

public class Calculator{
	private Strategy strategy;
	public void setStrategy(Strategy strategy) {
		this.strategy = strategy;
	}

	public int calculate(strategy) {
		return strategy.calculate(a, b);
	}
}

public class Client{
	public static void main(String[] args) {
		Calculator calculator = new Calculator();//实例化计算器”
		calculator.setStrategy(new Addition());
		calculator.calculate(1, 8);
 
	}
}

具体的计算策略由具体的strategy实现类决定,实现可替换,在Springboot开发中,用的对象注入 @Resource @Autowired , 声明的类型是接口,实际动作由具体实现类完成

16、状态模式

使用代码模拟交通灯状态变化,红灯和绿灯不能直接变化需要黄灯转化


public class State{
	void switchToGreen();
	void switchToYellow();
	void switchToRed();
}
//红灯
public class Red implements State {
	@Override
	public void switchToGreen(TrafficLight trafficLight) {
		System.out.print("红灯不能切换为绿灯");
	}
	@Override
	pubic void switchToYellow(TrafficLight trafficLight) {
		trafficLight.setState(new Yellow());
		System.out.print("黄灯亮起");
	}
	public void switchToRed(TrafficLight trafficLight) {
		System.out.print("已是红灯状态无需切换");
	}

}

同上实现黄灯 绿灯

public class TrafficLight {
	State state = new Red();//初始状态为红灯
	public void setState(State state) {
		this.state = state;
	}

	//切换为绿灯
	pubic void switchToGreen() {
		state.switchToGreen(this);
	}
	public void switchToRed() {
		state.switchToRed(this);
	}
	public void switchToYellow() {
		state.switchToYellow(this);
	}
}

public class Client{
	TrafficLight trafficLight = new TrafficLight();
	trafficLight.switchToYellow();
	trafficLight.switchToGreen();
	trafficLight.switchToYellow();
	trafficLight.switchToRed();
}

从类结构上看,状态模式与策略模式非常类似,其不同之处在于,策略模式是将策略算法抽离出来并由外部注入,从而引发不同的系统行为,其可扩展性更好;而状态模式则将状态及其行为响应机制抽离出来,这能让系统状态与行为响应有更好的逻辑控制能力,并且实现系统状态主动式的自我转换。状态模式与策略模式的侧重点不同,所以适用于不同的场景。总之,如果系统中堆积着大量的状态判断语句,那么就可以考虑应用状态模式,它能让系统原本复杂的状态响应及维护逻辑变得异常简单。状态的解耦与分立让代码看起来更加清晰、明了,可读性大大增强,同时系统的运行效率与健壮性也能得到全面提升。

17、备忘录模式

备忘录模式(Memento)则可以在不破坏元对象封装性的前提下捕获其在某些时刻的内部状态,并像历史快照一样将它们保留在元对象之外,以备恢复之用

18、命令模式

命令模式三个角色 被控制对象 命令对象 命令执行对象


public class Bulb{
	public void on{
		System.out.print("灯亮");
	}
	public void off() {
		System.out.print("灯灭");
	}
}
public interface Command{
	//执行
	void exe();
	//反向执行
	void unexe();
}

//灯泡命令专门用来控制灯泡
public class BulbCommand implements Command{
	private Bulb buld;

	public BulbCommand(Bulb buld){
		this.bulb = bulb;
	}
	@Override
	public void exe() {
		bulb.on();
	}
	@Override
	public void unexe() {
		bulb.off();
	}
}


//开关
public class Switcher{
	private Command command;
	public void setCommand(Command command) {
		this.command = command;
	}

	//按钮绑定命令
	public void buttonPush() {
		System.out.print("按下按钮");
		command.exe();
	}

	public void buttonPop() {
		System.out.print("按钮谈起");
		command.unexe();
	}
}

public class Client{
	public static void main(String[] args) {
		Swicher switcher = new Switcher();
		Bulb bulb = new Bulb();
		Command bulbCommand = new BulbCommand(bulb);
		switcher.setCommand(bulbCommand);
		switcher.buttonPush();
		switcher.buttonPop();
	}
}

上面代码示例中Switcher 类中可以引入一组command 然后编排命令执行

19、访问者模式

访问者模式主要解决数据与算法耦合的问题

@Data
public abstract class Products{
	private String name;
	private float price;
	public Products(String name, float price) {
		this.name = name;
		this.price = price;
	}

}

//糖果类
public class Candy extends Products {
	public Candy(String name, float price) {
		super(name, price);
	}
}

//红酒类
public class Wine extends Products {
	public Winde(String name, float price) {
		super(name, price);
	}
}

//蔬菜类
@Data
public class Fruit extends Products {
	public float weight;
	public Fruit(String name, float price, float weight) {
		super(name, price);
		this.weight = weight;
	}
}

商品数据类定义好后,就需要一个收银员角色对所有商品进行价格计算,而 收银员角色就是商品的访问者,不同的商品有不同的计价策略,收银员应该根据不同的商品类应用不同的计价方法

基于此,我们来思考一下如何设计访问者。我们先做出对商品类别的判断,能否用instanceof运算符判断商品类别呢?不能,否则代码里就会充斥着大量以“if”“else”组织的逻辑,显然太混乱。有些读者可能想到了使用多个同名方法的方式,以不同的商品类别作为入参来分别处理。没错,这种情况用重载方法再合适不过了。我们开始代码实战,首先定义一个访问者接口,为日后的访问者扩展打好基础

public interface Visitor {
   public void visit(Candy candy);// 糖果重载方法
   public void visit(Wine wine);// 酒类重载方法
   public void visit(Fruit fruit);// 水果重载方法
}

@Override
public class DiscountVisitor implements Visitor{
	public void visit(Candy candy) {
		//糖果类计算逻辑
	}
	public void visit(Wine wine) {
		//红酒类结算逻辑
	}
	public void visit(Friut fruit) {
		//蔬菜类计算逻辑
	}
}


public class Client(
	public static void main(String[] args) {
		List products = Arrays.asList(new Candy("大白兔",11.0), new Wine("干红"1000), new Fruit("芹菜"4.00));

	}	
	Visitor discountVisitor = new DiscountVisitor(LocalDate.of(2018, 1, 1));
	
       // 迭代购物车中的商品

       for (Product product : products) {

          discountVisitor.visit(product);// 此处会报错

       }

foreach循环只能用抽象商品类Product进行承接,可以看到此时引发的编译错误。重载方法自动派发不能再正常工作了,这是由于编译器对泛型化的商品类Product茫然无措,分不清到底是糖果还是酒,所以也就无法确定应该调用哪个重载方法了。

基于这种“主动亮明身份”的理念,我们对系统进行重构,之前定义的商品模块就需要作为“接待者”主动告知“访问者”自己的身份,所以它们要一定拥有“接待排查”的能力。我们定义一个接待者接口来统一这个行为标准

public interface Acceptable {
   // 主动接待访问者
   public void accept(Visitor visitor);
}
public class Candy extends Product implements Acceptable{
   	public Candy(String name, LocalDate producedDate, float price) {
      super(name, producedDate, price); 
		}
		@Override
    public void accept(Visitor visitor) {
        visitor.visit(this);// 把自己交给访问者
    }
}

public class Client{
	public static void main(String[] args) {
		List<Acceptable> products = Arrays.asList(
      new Candy("小兔奶糖",  20.00f),
      new Wine("老猫白酒", 1000.00f),
      new Fruit("草莓",  10.00f, 2.5f)
   		);

	   Visitor discountVisitor = new DiscountVisitor();
	   // 迭代购物车中的商品
	   for (Acceptable product : products) {
	      product.accept(discountVisitor);
	   }
  }
}

总之,访问者模式的核心在于对重载方法与双派发方式的利用,这是实现数据算法自动响应机制的关键所在。而对于其优秀算法的扩展是建立在稳定的数据基础之上的,对于数据多变的情况,我们就得对系统大动干戈了,所有的访问者重载方法都要被修改一遍,所以读者需要特别注意,对于这种情况并不推荐使用访问者模式 。

20、观察者模式

循环调用注册的监听器

21、解释器模式

终道

单一职责

见名知意

开闭原则

对扩展开放对修改关闭,简单说就是不要修改原有代码,而应该创建新的代码

里氏替换

“替换”则指的是父类与子类的可替换性。此原则指的是在任何父类出现的地方子类也一定可以出现,也就是说一个优秀的软件设计中有引用父类的地方,一定也可以替换为其子类

接口隔离

不要将接口定义成全能型的,否则实现类就需要神通广大,这样子降低了子类的灵活性,接口要尽量小粒度,往往一个接口对应一个职能,不能讲同一种职能散落在多个接口上

依赖倒置

我们知道,面向对象中的依赖是类与类之间的一种关系,如H(高层)类要调用L(底层)类的方法,我们就说H类依赖L类。依赖倒置原则(Dependency Inversion Principle)指高层模块不依赖底层模块,也就是说高层模块只依赖上层抽象,而不直接依赖具体的底层实现,从而达到降低耦合的目的。如上面提到的H与L的依赖关系必然会导致它们的强耦合,也许L任何细枝末节的变动都可能影响H,这是一种非常死板的设计。而依赖倒置的做法则是反其道而行,我们可以创建L的上层抽象A,然后H即可通过抽象A间接地访问L,那么高层H不再依赖底层L,而只依赖上层抽象A。这样一来系统会变得更加松散,这也印证了我们在“里氏替换原则”中所提到的“面向接口编程”,以达到替换底层实现的目的。

迪米特法则

最少知道原则

“当各种模式在我们的设计中变得“你中有我,我中有你”时,才达到了不拘泥于任何形式的境界”