本篇文章全部借鉴DOOP大神。
1. 设计模式遵循的原则
-
设计模式是面向接口编程的,而不是面向实现编程,用于扩展。
-
职责单一原则,一个类只能拥有一个单一的功能,并且这个功能只能由当前类全部封装起来。
-
对扩展开放,对修改关闭。
2. 创建型模式
1. 简单工厂
public class FoodFactory{ public static Food getFood(String foodName){ if(foodName.equals("ChineseFood")){ return new ChinesFood(); }else if(foodName.equals("AmericanFood")){ return new AmericanFood(); }else ... } }
2. 工厂模式
public interface Food{ void makeFood(); } public ChineseFood implements Food{ public void makeFood(){ System.out.printlen("ChineseFood..."); } } public AmericanFood implements Food{ public void makeFood(){ System.out.printlen("AmericanFood..."); } } public interface FoodFactory{ Food getFood(); } public ChineseFoodFactory implements FoodFacory{ Food getFood{ return new ChineseFood(); } } public AmericanFoodFactory implements FoodFacory{ Food getFood{ return new AmericanFood(); } }
3. 抽象工厂
抽象工厂主要再涉及到产品族的时间使用,比如拥有两家不同的电脑产商AMD和Intel,旗下拥有CpuFactory , MainFactory, AmdCpuFactory 和 IntelCpuFactory ,AmdMainFactory,IntelMainFactory 生产出来的物品之是不兼容的,所以我们是需要拥有一个具体的ComputerFactory 工厂来之间实现
4. 单例模式
饿汉模式:在第一次使用这个类的时间直接就将这个单例创建出来;
public class SingleObject{ private static singleObject=new SingleObject(); priate SingleObject(){} public static getInstance(){ return singleObject; } }
懒汉模式:使用双重判断是否为空来获取对象的实例
public class Single{ private static volatile Single instance=null; public static void getInstance(){ if(instance==null){ synchronized(Single.class){ if(instance==null){ this.instance=new Single(); } } } return instance } }
嵌套模式:就是在一个类中拥有已给静态内部类
public class Single { private Single(){} private static class Holder{ public static Single instance=new Single(); } public static getInstance(){ return Holder.instance; } }
5.构建者模式
什么是构建者模式?构建者模式的主要思想就是内部拥有一个Bulider用来对实体类的拷贝,并且可以做到增强
package gof; public class BuliderGof { public static void main(String[] args) { User 打豆豆 = User.bulider().age(23) .hobby("打豆豆") .pwd("1111").userName( "wangzhiqiang" ).bulid(); System.out.println(打豆豆); } } class User { public static Bulider bulider; private String userName; private String pwd; private String hobby; private Integer age; //直接将构造方法私有化 private User() { } private User(String name, String pwd, String hobby, Integer age) { this.userName = name; this.pwd = pwd; this.hobby = hobby; this.age = age; } public static Bulider bulider() { return new Bulider(); } @Override public String toString() { StringBuilder stringBuilder = new StringBuilder(); return stringBuilder.append("密码是:"+pwd).append("用户名是:"+userName).append("年龄是"+age).append(hobby).toString(); } public static class Bulider { private String userName; private String pwd; private String hobby; private Integer age; public Bulider userName(String name) { this.userName = name; return this; } public Bulider pwd(String pwd) { this.pwd = pwd; return this; } public Bulider hobby(String hobby) { this.hobby = hobby; return this; } public Bulider age(Integer age) { this.age = age; return this; } public User bulid() { return new User(userName, pwd, hobby, age); } } }
6. 原型模式
原型模式很简单: 就是基于一个原型对象 来进行克隆新的实例,也就是深度克隆。
创建型模式总结
简单工厂模式最简单;工厂模式在简单工厂模式的基础上增加了选择工厂的维度,需要第一步选择合适的工厂;抽象工厂模式有产品族的概念,如果各个产品是存在兼容性问题的,就要用抽象工厂模式。单例模式就不说了,为了保证全局使用的是同一对象,一方面是安全性考虑,一方面是为了节省资源;建造者模式专门对付属性很多的那种类,为了让代码更优美;原型模式用得最少,了解和 Object 类中的 clone() 方法相关的知识即可。
2. 结构性模式
1. 代理模式
代理模式最重要的就是使用一个代理类来隐藏具体的实现类的实现细节,并且对实现类做相应的增强;
核心点就是在代理类中拥有一个
package gof; import javax.sound.midi.Soundbank; /** * strong * 2024/3/25 **/ public class ProxyGof { public static void main(String[] args) { FoodService service=new FoodServiceProxy(); service.makeNoodles(); service.makeNoodles(); } static class FoodServiceProxy implements FoodService { FoodService foodService = new FoodServiceImpl(); @Override public Food makeChicken() { Food chicken = foodService.makeChicken(); System.out.println("代理增强。。。"); return chicken; } @Override public Food makeNoodles() { Food noodles = foodService.makeNoodles(); System.out.println("代理增强"); return noodles; } } } ========共同实现的FoodService====== interface FoodService{ Food makeChicken(); Food makeNoodles(); } class FoodServiceImpl implements FoodService{ @Override public Food makeChicken() { Food chicken = new Food(); chicken.setDesc("鸡肉"); chicken.setMoney(100); System.out.println(chicken); return chicken; } @Override public Food makeNoodles() { Food noodle = new Food(); noodle.setDesc("面条"); noodle.setMoney(10); System.out.println(noodle); return noodle; } }
2. 适配器模式
通用适配器
通用适配器主要解决的是拥有非常多的接口,我们在实现接口的时间用不到全部的功能,这个时间就需要来借助适配器。
下面的接口中拥有非常多的方法
public interface FileAlterationListener { void onStart(final FileAlterationObserver observer); void onDirectoryCreate(final File directory); void onDirectoryChange(final File directory); void onDirectoryDelete(final File directory); void onFileCreate(final File file); void onFileChange(final File file); void onFileDelete(final File file); void onStop(final FileAlterationObserver observer); }
然后我们可以定制他的适配器
public class FileAlterationListernerAdaptor implements FileAlterationListener { public void onStart(final FileAlterationObserver observer) { } public void onDirectoryCreate(final File directory) { } public void onDirectoryChange(final File directory) { } public void onDirectoryDelete(final File directory) { } public void onFileCreate(final File file) { } public void onFileChange(final File file) { } public void onFileDelete(final File file) { } public void onStop(final FileAlterationObserver observer) { } }
接着,我们只需要实现适配器中自己用到的方法就可以
public class Filemontior extends FileAlterationListernerAdaptor{ public void onFileCreate(final File file) { // 文件创建 doSomething(); } public void onFileDelete(final File file) { // 文件删除 doSomething(); } }
对象适配器适配器
适配器的主要思想就是利用唯一拥有的一个类去替代我们想要利用的类。这两个类之间没有任何的关系是组合的一种实现
public interface Duck{ void fly(); void duckSong(); } public interface Cock{ void fly(); void cockSong(); } public class WildCock{ public void fly{ System.out.println("鸡在飞。。。") } public void cockSong(){ System.out.println("鸡在唱歌。。。"); } } public class CockAdaptor Duck{ private Cock cock; public CockAdaptor(Cock cock){ this.cock=cock; } public void fly{ cock.fly(); } public void duckSong{ cock.cockSong(); } } public static void main(String [] args){ //有一只野鸡 Cock wildCock=new WildCock(); //通过鸡的适配器 来模拟鸭子 Duck duck= new CockAdaptor(wildCock); }
类适配器
类适配器的核心思想就是通过继承的方式来获取对象的所有方法.
适配器总结
-
类适配和对象适配的异同
一个采用继承,一个采用组合;
类适配属于静态实现,对象适配属于组合的动态实现,对象适配需要多实例化一个对象。
总体来说,对象适配用得比较多。
-
适配器模式和代理模式的异同
比较这两种模式,其实是比较对象适配器模式和代理模式,在代码结构上,它们很相似,都需要一个具体的实现类的实例。但是它们的目的不一样,代理模式做的是增强原方法的活;适配器做的是适配的活,为的是提供“把鸡包装成鸭,然后当做鸭来使用”,而鸡和鸭它们之间原本没有继承关系。
3. 桥梁模式
桥梁模式就是通过内置的一个接口来使用他的所有的实现的子类。
首先需要有一个桥梁,他是一个接口,定义提供的方法。
public interface DrawAPI { public void draw(int radius, int x, int y); }
然后是一些列的实现类
public class RedPen implements DrawAPI { @Override public void draw(int radius, int x, int y) { System.out.println("用红色笔画图,radius:" + radius + ", x:" + x + ", y:" + y); } } public class GreenPen implements DrawAPI { @Override public void draw(int radius, int x, int y) { System.out.println("用绿色笔画图,radius:" + radius + ", x:" + x + ", y:" + y); } } public class BluePen implements DrawAPI { @Override public void draw(int radius, int x, int y) { System.out.println("用蓝色笔画图,radius:" + radius + ", x:" + x + ", y:" + y); } }
接着我们来定义一个抽象类,需要使用这个 桥梁的子类的功能,注意这个抽象类的所有的成员变量和他的方法都是使用 protected 来实现的。
public abstract Shape{ protected DrawApi drawApi; protected void Shape(DrawApi draw){ this.drawApi=draw; } //定义需要实现的方法 public abstract void draw(); }
然后定义抽象类的实现类
// 圆形 public class Circle extends Shape { private int radius; public Circle(int radius, DrawAPI drawAPI) { super(drawAPI); this.radius = radius; } public void draw() { drawAPI.draw(radius, 0, 0); } } // 长方形 public class Rectangle extends Shape { private int x; private int y; public Rectangle(int x, int y, DrawAPI drawAPI) { super(drawAPI); this.x = x; this.y = y; } public void draw() { drawAPI.draw(0, x, y); } }
4.装饰器模式
装饰器模式等同于代理模式的增强功能,但是对于有许多功能的增强,我们只能通过对代理模式的不断代理嵌套,在这个时间,我们就可以通过装饰器模式来增强多个功能.
首先需要一个抽象类,主要用于代码的复用,来规定一种所属的关系。
public abstract class Beverage{ // 返回描述 public abstract String getDescription(); // 返回价格 public abstract double cost(); }
然后我们来定义具体的实现类 BlackTea,Coffee
public class BlackTea extends Beverage{ // 返回描述 public String getDescription(){ return "红茶"; } // 返回价格 public double cost(){ return 1.00; } }
public class Coffee extends Beverage{ // 返回描述 public String getDescription(){ return "咖啡"; } // 返回价格 public double cost(){ return 2.00; } }
定义调料类,也就是装饰器
//调料装饰器 public abstract class Condiment extends Beverage{ }
//开始定义 调料的具体实现类,也就是具体的调料类,用于添加到BlackTea ,Coffee中
public class Lemon extends Condiment{ priavate Beverage beverage; public Lomen(Beverage beverage){//这里是一个重点,我们需要对这个装饰器中添加一个具体的实现类 this.beverage=beverage; } // 返回描述 public String getDescription(){ return beverage.getDescription+"柠檬"; } // 返回价格 public double cost(){ return beverage.cost+0.50; } } public class Mango extends Condiment { private Beverage bevarage; public Mango(Beverage bevarage) { this.bevarage = bevarage; } public String getDescription() { return bevarage.getDescription() + ", 加芒果"; } public double cost() { return beverage.cost() + 3; // 加芒果需要 3 元 } } ...// 给每一种调料都加一个类
看看客户端的调用
public static void main(String [] args){ //开始需要一个基础类 Beverage beverage=new BlackTea(); // 开始装饰 tea=new Lemon(tea);//添加一份柠檬 tes=new Condiment(tea);//添加一份芒果 System.out.println(beverage.getDescription() + " 价格:¥" + beverage.cost()); //"绿茶, 加柠檬, 加芒果 价格:¥16" }
再来看看,制作出一份 加两个柠檬,一个芒果的 红茶
Beverage beverage=new Lemon(new Lemon(new Condiment(new BlackTea) ));
5.门面模式
门面模式需要注意的就是直接在类中拥有想要实现功能的所有的实现类。
先来看看日常的使用
public interface Shape { void draw(); }
然后定义实现类
public class Circle implements Shape { @Override public void draw() { System.out.println("Circle::draw()"); } } public class Rectangle implements Shape { @Override public void draw() { System.out.println("Rectangle::draw()"); } }
定义一个门面
public class ShapeMaker { private Shape circle; private Shape rectangle; private Shape square; public ShapeMaker() { circle = new Circle(); rectangle = new Rectangle(); square = new Square(); } /** * 下面定义一堆方法,具体应该调用什么方法,由这个门面来决定 */ public void drawCircle(){ circle.draw(); } public void drawRectangle(){ rectangle.draw(); } public void drawSquare(){ square.draw(); } }
3.行为模式
1.策略模式
策略模式和桥梁模式非常的相似,但是桥梁模式更加的抽象,他需要添加一个抽象类来规定使用 drawApi
首先定义一个策略
public interface Strategy { public void draw(int radius, int x, int y); }
定义策略的实现类
public class RedPen implements Strategy { @Override public void draw(int radius, int x, int y) { System.out.println("用红色笔画图,radius:" + radius + ", x:" + x + ", y:" + y); } } public class GreenPen implements Strategy { @Override public void draw(int radius, int x, int y) { System.out.println("用绿色笔画图,radius:" + radius + ", x:" + x + ", y:" + y); } } public class BluePen implements Strategy { @Override public void draw(int radius, int x, int y) { System.out.println("用蓝色笔画图,radius:" + radius + ", x:" + x + ", y:" + y); } }
定义使用策略的类
public class Context { private Strategy strategy; public Context(Strategy strategy){ this.strategy = strategy; } public int executeDraw(int radius, int x, int y){ return strategy.draw(radius, x, y); } }
看看客户端的调用
public static void main(String[] args) { Context context = new Context(new BluePen()); // 使用绿色笔来画 context.executeDraw(10, 0, 0);
2. 观察者模式
首先我们需要定义一个主题,主题中包含了所有观察者的实例,并且在主题中的数据发生变化的时间,会通知订阅该主题的所有观察者。
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{ private Subject subject; public 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 subject=new Subject(); Observer observer=new HesaObserver(subject); subject.setState(1);//在修改数据的时候,会直接将所有订阅该主题的方法全部通知到位。 }
3. 责任链模式
在责任链模式中非常适应于 一种流程的校验,比如说:需要判断一个用户使用中奖,第一步需要判断他是否具有中奖资格,然后判断他是否在指定的区域之内,最后判断奖品的数来那个是否大于0 ,最终才可以确定他是否获奖。
首先我们需要来定义链表的节点
public abstract class RuleHandler{ RuleHandler rule;//定义的后继节点 public void setHandler(Rulehandler rule){ this.rule=rule; } public RuleHandler getHandler(){ return rule; } public abstract void apply(); }
接下来我们需要定义每一个具体的规则实现。
校验是否为新用户。
public class NewUserRuleHandler extends RuleHandler { public void apply(Context context) { if (context.isNewUser()) { // 如果有后继节点的话,传递下去 if (this.getSuccessor() != null) { this.getSuccessor().apply(context); } } else { throw new RuntimeException("该活动仅限新用户参与"); } } }
校验地区是否可以参加
public class LocationRuleHandler extends RuleHandler { public void apply(Context context) { boolean allowed = activityService.isSupportedLocation(context.getLocation); if (allowed) { if (this.getSuccessor() != null) { this.getSuccessor().apply(context); } } else { throw new RuntimeException("非常抱歉,您所在的地区无法参与本次活动"); } } }
校验商品是否还拥有库存
public class LimitRuleHandler extends RuleHandler { public void apply(Context context) { int remainedTimes = activityService.queryRemainedTimes(context); // 查询剩余奖品 if (remainedTimes > 0) { if (this.getSuccessor() != null) { this.getSuccessor().apply(userInfo); } } else { throw new RuntimeException("您来得太晚了,奖品被领完了"); } } }
来看看客户端调用
public static void main(String[] args) { RuleHandler newUserHandler = new NewUserRuleHandler(); RuleHandler locationHandler = new LocationRuleHandler(); RuleHandler limitHandler = new LimitRuleHandler(); // 假设本次活动仅校验地区和奖品数量,不校验新老用户 locationHandler.setSuccessor(limitHandler); locationHandler.apply(context); }
4. 模板方法
模板方法主要定义了如何使用这些方法,每一个方法具体的实现是由子类实现的
public abstract class AbstractTemplate { // 这就是模板方法 public void templateMethod() { init(); apply(); // 这个是重点 end(); // 可以作为钩子方法 } protected void init() { System.out.println("init 抽象层已经实现,子类也可以选择覆写"); } // 留给子类实现 protected abstract void apply(); protected void end() { } }
定义子类
public class ConcreteTemplate extends AbstractTemplate { public void apply() { System.out.println("子类实现抽象方法 apply"); } public void end() { System.out.println("我们可以把 method3 当做钩子方法来使用,需要的时候覆写就可以了"); } }
客户端调用
public static void main(String[] args) { AbstractTemplate t = new ConcreteTemplate(); // 调用模板方法 t.templateMethod(); }
5.状态模式
状态模式的关键在于,我们不用关心一个事件Context该去执行哪些操作,而是去关心他英爱具有哪些操作
首先我们来定义一个事件类
public class Context{ private State state; public setState(State state){ this.state=state; } public State getState(){ return state; } }
首先我们来定义一个状态基类
public interface State{ void doSomeThing(); }
定义减库存的状态
public class DuceState implements State{ //首先我们需要一个事件 public void doSomeThing(Context context){ context.setState(this); //开始减库存操作 } }
定义补库存的类
public class RevertState implements State { public void doAction(Context context) { System.out.println("给此商品补库存"); context.setState(this); //... 执行加库存的具体操作 } public String toString() { return "Revert State"; } }