设计模式
23种设计模式:单例模式、工厂方法模式、抽象工厂模式、模板方法模式、建造者模式、代理模式、原型模式、中介者模式、命令模式、责任链模式、装饰模式、策略模式、适配器模式、迭代模式、组合模式、观察者模式、门面模式、备忘录模式、访问者模式、状态模式、解释器模式、享元模式、桥梁模式
1.单例模式
定义:确保一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。
优点:1.需要频繁的创建和销毁对象时,减少了内存开支。
2.避免资源的多重占用:一个类只产生一个对象,避免了同时写操作。
3.可以在系统设置全局访问点,优化、共享资源
缺点:1.单例模式没有接口,扩展困难(接口或抽象类不能实例化,但特殊情况下单例模式也可以实现接口或被继承)
2.单例模式对测试不利,因为如果有单例没开发完,就无法进行测试
3.单例模式和单一职责原则有冲突,单一职责强调一个类只有一个职责,单例模式强调一个类只能创建一个对象,不考虑是不是一个职责。
使用场景:要求一个类有且仅有一个对象,出现多个对象业务就会出现bug,此时需要用单例模式。具体情况如下:
1.要求生产唯一序列号的环境
2.一个项目中需要一个共享访问点或共享数据,例如一个计数器,但计数器的值不需要放到数据库中,使用单例可以保持计数器的值,且线程安全
3.创建一个对象需要消耗的资源过多,如访问IO和数据库等资源
4.需要定义大量的静态对象或静态方法(也可以直接声明为static方法或定义在接口中)
有上限的单例模式:产生固定数量的对象模式
优点:修正单例模式可能存在的性能问题,提高系统的响应速度。例如读取文件,需要在系统启动时完成初始化工作,在内存中启动固定数量的reader,可以提高读文件的速度。
单例模式实践:
在Spring中,每个Bean都是单例的,这样的优点是Spring容器可以管理这些Bean的生命周期,决定什么时候创建出来,什么时候销毁,销毁时需要如何处理等。
自己理解:单例模式即一个类只产生一个实例,该实例可以被整个项目调用。实现只有一个实例的 关键在于构造方法必须私有化, 这样才能避免被其他的类创建对象,将构造方法私有化,然后提供一个公共的访问方法,返回该类的实例对象,且返回的这个对象只会被创建一次(使用static和final修饰)。单例模式的优点就是节省创建对象时的消耗,可以提供一个全局的唯一对象或值,但是不利于以后的扩展。所以使用场景应该是创建类比较消耗性能或项目中只需要创建一个类的时候或用来做累计的情况下用。
/**
* 饿汉单例模式:高并发时可以用
* 通用的单例模式
* @author dell
*
*/
public class SingletonEHan {
//创建一个单例 对象,类被调用时创建对象
private static final SingletonEHan singleton = new SingletonEHan();
//私有化构造方法,使其只能在该类里面被调用,外界不能调用
private SingletonEHan(){}
//提供一个公共的访问方法,返回该类的实例对象
public static SingletonEHan getSingleton(){
return singleton;
}
//一些其他的方法,尽量都是static
public static void doSomething(){
System.out.println("一些其他方法");
}
}
/**
* 懒汉式单例模式:使用时才会创建对象,但不适合高并发的情况
* @author dell
*
*/
public class SingletonLanHan {
private SingletonLanHan(){}
//使用jvm创建对象,避免线程不安全
private static class InstanceHolder{
private static SingletonLanHan instance = new SingletonLanHan ();
}
public static SingletonLanHan getInstance(){
return InstanceHolder.instance;
}
}
/**
* 有上限的单例模式
* @author dell
*
*/
public class Singleton {
//定义最多能产生的实例数量
private static int maxNum=3;
//每个对象都有名字,使用ArrayList来容纳每个对象的自有属性
private static List<String> nameList = new ArrayList<>();
//定义一个列表,容纳所有的对象实例
private static List<Singleton> singletonList = new ArrayList<>();
//当前对象序列号
private static int countNum = 0;
//产生所有的对象-使用静态代码块
static{
for (int i = 0; i < maxNum; i++) {
singletonList.add(new Singleton("对象i+1"));
}
}
//私有化构造函数
private Singleton(){}
//私有化构造函数--带参数
private Singleton(String name){
nameList.add(name);
}
//随机获得一个对象--一个公共的访问方法
public static Singleton getSingleton(){
//生成一个随机序列号
Random random = new Random();
countNum = random.nextInt(maxNum);
//返回一个对象
return singletonList.get(countNum);
}
}
工厂模式
定义:定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类。
优点:
1.封装性好,降低了模块间的耦合度,调用者只需要知道具体产品的产品类名就可以生产 产品类。
2.扩展性好,增加产品类时,只需要修改具体的工厂类或者扩展一个工厂类就行。
3.屏蔽产品类(该特点很重要),调用者不需要关心产品类里面是什么,如何变化的等等。
4.工厂模式是典型的解耦框架,高层模块只需要知道产品抽象类和具体产品名,其他的不需要知道,符合迪米特法则。
使用场景:
1.是new对象的代替品。
2.需要灵活可扩展的框架时,可以考虑工厂模式
3.可以用在异构项目中
扩展
简单工厂模式:
一个模块只需要一个工厂类,没有必要把它生产出来,使用静态方法也可以(不用创建对象,直接通过类名调用)。
简单工厂也叫静态工厂,扩展比较困难,不符合开闭原则,但很实用。
多工厂类:
复杂的应用中,一个工厂生产很多产品类,比较乱,所以使用多个工厂生产产品类,增加一个协调类,避免调用者直接与子工厂交流,协调类封装子工厂,向高层模块提供一个统一的访问接口。
工厂模式创建单例模式:
单例模式的核心是内存中一个类只存在一个对象,单例模式是在只能该类中创建对象,且在该类中只new一次对象后给外界提供。工厂模式也可以实现,只需要保证该类不能被外界随意创建,且只会被创建一个就可以,所以还是私有化构造方法,在工厂类中,通过反射获取类,设置构造方法可以被访问,然后创建对象并向外界提供,所以用static修饰,确保只会被创建一次。
自己理解:
工厂模式即通过对象名称通过反射来动态的创建对象,代替new。创建产品接口和具体产品类、抽象工厂和具体工厂类,在具体工厂类中通过反射创建对象。这样高层模块调用时,只需要调用工厂类接口,传具体产品类名字,这样降低了耦合性,方便扩展。
通过工厂模式代码:
/**
* 通用工厂模式-抽象产品类
* @author dell
*
*/
public abstract class Product {
//产品类的公共方法
public void method1(){
System.out.println("通用方法");
}
//抽象方法
public abstract void method2();
}
/**
* 通用工厂模式-具体产品类
* @author dell
*
*/
public class ConcreteProduct1 extends Product {
@Override
public void method2() {
System.out.println("ConcreteProduct1产品的方法");
}
}
/**
* 通过工厂模式-抽象工厂类
* @author dell
*
*/
public abstract class Create {
//创建一个产品对象,其输入的参数可自行设置,通常为String、Enum、Class等,也可以为空--具体怎么产生对象,由具体实现类决定
public abstract <T extends Product> T createProduct(Class<T> c);
}
/**
* 通用工厂方法--场景类
* @author dell
*
*/
public class Client {
public static void main(String[] args) {
Create create = new ConcreteCreator();
Product product = create.createProduct(ConcreteProduct1.class);
System.out.println("后续业务处理");
}
}
简单工厂模式代码:
/**
* 产品类 接口
* @author dell
*/
public interface Human {
public void getColor();
public void talk();
}
/**
* 简单工厂 具体产品类
* @author dell
*/
public class WhiteHuman implements Human {
@Override
public void getColor() {
System.out.println("具体产品类方法1");
}
@Override
public void talk() {
System.out.println("具体产品类方法2");
}
}
/**
* 简单工厂--工厂类
* @author dell
*/
public class HumanFactory {
public static <T extends Human> T createHuman(Class<T> c){
Human human = null;
try {
human = (Human) Class.forName(c.getName()).newInstance();
} catch (InstantiationException | IllegalAccessException | ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return (T)human;
}
}
/**
* 简单工厂 场景类
* @author dell
*/
public class Client {
public static void main(String[] args) {
Human createHuman = HumanFactory.createHuman(WhiteHuman.class);
createHuman.getColor();
createHuman.talk();
}
}
工厂模式代替单例模式 代码
/**
* 工厂模式代替单例模式---单例类
* @author dell
*/
public class Singleton {
private Singleton(){}
public void method(){
System.out.println("其他方法");
}
}
/**
* 工厂模式代替单例模式--生成单例的工厂类
* @author dell
*/
public class SingletonFactory {
private static Singleton singleton;
static{
//通过反射生产单例
try {
//获取需要生产的类
Class c1 = Class.forName(Singleton.class.getName());
//获得无参构造
Constructor constructor = c1.getDeclaredConstructor();
//设置无参构造可以访问
constructor.setAccessible(true);
//生产一个对象实例
singleton = (Singleton) constructor.newInstance();
} catch (Exception e) {
e.printStackTrace();
}
}
//提供一个访问方法,向外界提供产生的对象
public static Singleton getSingleton(){
return singleton;
}
}
抽象工厂模式
定义:为一组相关或相互依赖的对象提供一个接口,而且无需指定他们的具体类
优点:
1.封装性好。
2.产品族内的约束为非公开状态。
缺点:产品族扩展困难(产品等级扩展方便)。
使用场景:一个对象族都有相同的约束,可以使用抽象工厂模式。
参考:https://blog.youkuaiyun.com/Olive_ZT/article/details/78861388
https://www.cnblogs.com/geek6/p/3951677.html
自己理解:抽象工厂模式跟工厂模式中的多工厂模式一样,工厂模式是生产一个具体的产品对象,当产品的种类比较多时,使用同一个工厂生产会混乱,不易维护,所以使用多个工厂,每个工厂生产一种产品对象。
模板模式
定义:定义一个操作中的算法或框架,而将一些步骤延迟到子类中。使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
优点:
1.封装不变部分,扩展可变部分(不变部分可封装到父类,扩展的部分通过继承来扩展)
2.提供公共部分代码,便于维护
3.行为由父类控制,之类实现
缺点:子类执行的结果影响父类的结果,增加代码阅读难度。
使用场景:
1.多个子类有公共的方法,并且逻辑相同时。
2.重要、复杂的算法,可以把核心算法设计为模板方法,相关细节功能由子类实现。
3.重构时,经常使用模板方法,将相同代码抽取到父类中,通过钩子方法约束行为。
扩展:
模板方法里面加钩子方法:由子类的一个方法的返回值决定公共部分的执行结果。
自己理解:模板方法就是对继承的一个应用,在模板方法中按照一定的规则和顺序调用基本方法。在父类中定义一些基本方法和一个(一些)模板方法,子类实现或重写基本方法,模板方法(父类中)按一定的顺序调用子类的基本方法,为防止恶意操作, 一般模板方法都加上final关键字,不允许被复写。
/**
* 抽象模板类
* @author dell
*
*/
public abstract class HummerModel {
/*
* 基本方法
*/
protected abstract void start();
protected abstract void stop();
protected abstract void alarm();
protected abstract void engineBoom();
/*
* 模板方法
*/
final public void run(){
this.start();
this.engineBoom();
//应用钩子方法
if(this.isAlarm()){
this.alarm();
}
this.stop();
}
//钩子方法
protected boolean isAlarm(){
return true;
}
}
/**
* 模板方法子类1
* @author dell
*
*/
public class HummerModel1 extends HummerModel {
private boolean alarmFlag = true;//要喇叭
@Override
protected void start() {
System.out.println("汽车1启动");
}
@Override
protected void stop() {
System.out.println("汽车1停止");
}
@Override
protected void alarm() {
System.out.println("汽车1喇叭");
}
@Override
protected void engineBoom() {
System.out.println("汽车1引擎声音");
}
//钩子方法重新
protected boolean isAlarm(){
return this.alarmFlag;
}
//客户端决定钩子方法是否执行
public void setAlarm(boolean isAlarm){
this.alarmFlag = isAlarm;
}
}
/**
* 模板方法子类2
* @author dell
*
*/
public class HummerModel2 extends HummerModel {
@Override
protected void start() {
System.out.println("汽车2启动");
}
@Override
protected void stop() {
System.out.println("汽车2停止");
}
@Override
protected void alarm() {
System.out.println("汽车2鸣笛");
}
@Override
protected void engineBoom() {
System.out.println("汽车2引擎声音");
}
//钩子方法-默认没有喇叭
protected boolean isAlarm() {
return false;
}
}
建造者模式
定义:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
优点:
1.封装性好。
2.建造者独立,容易扩展。
3.便于控制细节风险。
使用场景:
1.相同的方法,不同的执行顺序,产生不同的结果时,采用建造者模式。
2.多个部件或零件,都可以装配到一个对象中,但是产生的运行结果又不相同时,则可以使用该模式。
3.产品类非常复杂,或者产品类中的调用顺序不同产生不同的效能,可以使用建造者模式。
建造者模式和工厂模式区别:建筑者关注的是顺序,零件的组装顺序不同,对象功效就不同。建造者模式最主要的功能是基本方法的调用顺序安排,顺序不同 产生的对象也就不同,而工厂方法则重点是创建,创建零件是它的主要职责,组装顺序不关心。模板模式的重点是方法的调用。
/**
* 产品类 车辆模型的抽象类(模板方法的父类)
* @author dell
*
*/
public abstract class CarModel {
//规定执行顺序的参数
private List<String> sequence = new ArrayList<>();
//车辆启动
protected abstract void start();
//车辆停止
protected abstract void stop();
//喇叭
protected abstract void alarm();
//引擎会响
protected abstract void engineBoom();
//车辆开始跑--模板方法的模板方法
public final void run(){
//开始循环执行顺序,谁在前,就先执行谁
for (int i = 0; i < this.sequence.size(); i++) {
String actionName = this.sequence.get(i);
if(actionName.equalsIgnoreCase("start")){
this.start();
}else if(actionName.equalsIgnoreCase("stop")){
this.stop();
}else if(actionName.equalsIgnoreCase("alarm")){
this.alarm();
}else if(actionName.equalsIgnoreCase("engineBoom")){
this.engineBoom();
}
}
}
//把传递过来的执行顺序传递到类内
public final void setSequence(List<String> sequence){
this.sequence = sequence;
}
}
/**
* 产品类 奔驰模型(模板方法的子类)
* @author dell
*/
public class BenzModel extends CarModel {
@Override
protected void start() {
System.out.println("奔驰启动");
}
@Override
protected void stop() {
System.out.println("奔驰停止");
}
@Override
protected void alarm() {
System.out.println("奔驰喇叭。。。");
}
@Override
protected void engineBoom() {
System.out.println("奔驰引擎...");
}
}
/**
* 产品类 宝马车模型(模板方法子类)
* @author dell
*/
public class BMWModel extends CarModel {
@Override
protected void start() {
System.out.println("宝马车启动...");
}
@Override
protected void stop() {
System.out.println("宝马车停止...");
}
@Override
protected void alarm() {
System.out.println("宝马车喇叭...");
}
@Override
protected void engineBoom() {
System.out.println("宝马车引擎...");
}
}
/**
* 抽象建造者
* @author dell
*/
public abstract class CarBuilder {
//创造产品需要的组装顺序
public abstract void setSequence(List<String> sequence);
//设置完组装顺序后,直接拿到这个车辆模型
public abstract CarModel getCarModel();
}
/**
* 具体建造者 奔驰车组装者
* @author dell
*/
public class BenzBuilder extends CarBuilder {
private BenzModel benz = new BenzModel();
@Override
public void setSequence(List<String> sequence) {
this.benz.setSequence(sequence);
}
@Override
public CarModel getCarModel() {
return this.benz;
}
}
/**
* 具体建造者 宝马车组装者
* @author dell
*/
public class BMWBuilder extends CarBuilder {
private BMWModel bmw = new BMWModel();
@Override
public void setSequence(List<String> sequence) {
this.bmw.setSequence(sequence);
}
@Override
public CarModel getCarModel() {
return this.bmw;
}
}
/**
* 导演类
* @author dell
*/
public class Director {
//执行顺序
private List<String> sequence = new ArrayList<>();
//奔驰建造者
private BenzBuilder benzBuilder = new BenzBuilder();
//宝马建造者
private BMWBuilder bmwBuilder = new BMWBuilder();
//A类型的奔驰车模型,先启动,再停止,其他没有
public BenzModel getABenzModel(){
//清理场景
this.sequence.clear();
//添加执行顺序
this.sequence.add("start");
this.sequence.add("stop");
//按照顺序返回一个奔驰车
this.benzBuilder.setSequence(this.sequence);
return (BenzModel) this.benzBuilder.getCarModel();
}
//B类型的奔驰车模型,先引擎,再启动,然后停止
public BenzModel getBBenzMode(){
//清理场景
this.sequence.clear();
//添加执行顺序
this.sequence.add("engine boom");
this.sequence.add("start");
this.sequence.add("stop");
this.benzBuilder.setSequence(this.sequence);
return (BenzModel) this.benzBuilder.getCarModel();
}
//C类型的宝马车先按喇叭,在启动,之后停止
public BMWModel getCBMWModel(){
this.sequence.clear();
this.sequence.add("alarm");
this.sequence.add("start");
this.sequence.add("stop");
this.bmwBuilder.setSequence(sequence);
return (BMWModel) this.bmwBuilder.getCarModel();
}
//D类型的宝马车只有一个功能,启动就跑,永不停止
public BMWModel getDBMWNodel(){
this.sequence.clear();
this.sequence.add("start");
this.bmwBuilder.setSequence(sequence);
return (BMWModel) this.bmwBuilder.getCarModel();
}
}
/**
* 场景类
* @author dell
*/
public class Client {
public static void main(String[] args) {
Director director = new Director();
//生产1千辆A类型的车
for (int i = 0; i < 1000; i++) {
director.getABenzModel().run();
}
//生产1千辆B类型的车
for (int i = 0; i < 1000; i++) {
director.getBBenzMode().run();
}
//生产1千辆C类型的车
for (int i = 0; i < 1000; i++) {
director.getCBMWModel().run();
}
}
}
代理模式
原型模式
定义:用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。即不通过new来产生一个对象,而是通过对象复制来实现。
优点:
1.性能优良:比直接new一个对象性能好的多
2.逃避构造函数的约束:直接在内存中拷贝,构造函数不会执行,需要大家在实际应用中考虑。
使用场景:
1.资源优化场景
2.性能和安全要求的场景:通过new产生一个对象需要非常繁琐的数据准备或访问权限,可以使用原型模式
3.一个对象多个修改场景:一个对象需要提供给其他对象访问,而且各个调用者可能都需要修改其值时,可以考虑使用原型模式拷贝多个对象供调用者使用。
核心:原型模式的核心是clone方法,通过该方法进行对象的拷贝。
自己理解:原型模式就是不使用new创建对象,而是使用复制的方式在堆内存中以二进制流的形式直接进行拷贝,重新分配一个内存块,所以使用原型模式时构造函数不会被执行。实现时需要在代码中实现Cloneable接口(Java自带的一个接口,该接口中没有任何方法,只是起标记的作用,标记实现该接口的类可以被复制),在被复制的类中重写clone()方法(该方法是重写的Object里的clone()).原型模式很少单独使用,一般是和工厂方法模式一起出现。
/**
* 原型模式 :通用代码
* @author dell
*/
public class PrototypeClass implements Cloneable{
//其他的方法。。。。
//重新Object类的clone方法
@Override
public PrototypeClass clone(){
PrototypeClass prototypeClass = null;
try {
prototypeClass = (PrototypeClass) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return prototypeClass;
}
}
/**
* 原型模式场景类
* @author dell
* */
public class Client {
public static void main(String[] args) {
PrototypeClass prototypeClass = new PrototypeClass();
System.out.println("原始的对象"+prototypeClass);
PrototypeClass clone = prototypeClass.clone();
System.out.println("原型模式拷贝的对象"+clone);
}
}
浅拷贝和深拷贝:Object类提供的clone()方法只拷贝对象本身,其对象内部的数组、引用对象等都不拷贝,还是指向原生对象的内部元素地址,这种拷贝是浅拷贝。将对象和对象的私有变量进行独立的拷贝,叫深拷贝。深拷贝和浅拷贝建议不要混合使用。
使用原型模式时,引用的成员变量必须满足两个条件才不会被拷贝:1.是类的成员变量,而不是方法变量。2.必须是一个可变的引用对象,而不是原始类型或不可变对象。所以,要使用clone方法,类的成员变量上不要增加final关键字,因为final不能被重新赋值。
中介者模式
定义:用一个中介对象封装一系列的对象交互,中介者使各对象不需要显示地相互作用,从而使其松耦合,而且可以独立的改变它们之间的交互。
组成部分:抽象中介者、具体中介者、同事角色
优点:减少类之间的依赖,把一对多的依赖变成一对一的依赖,降低了类之间的耦合。
缺点:中介者会膨胀的很大,而且逻辑复杂,原本N个对象直接的相互依赖关系转换为中介者和同事类的依赖关系,同事类越多,中介者的逻辑就越复杂。
使用场景:(中介者应合理使用,不要所有的依赖都使用,因为类之间一定会有依赖)。中介者模式适用于多个对象之间紧密耦合的情况(紧密耦合:在类图中出现了蜘蛛网状结构,这种情况一定要考虑使用中介者模式,有利于把蜘蛛网梳理为星型结构,使复制关系清晰)。
1.N个对象之间产生了相互的依赖关系(N>2).
2.多个对象有依赖关系,但依赖的香味尚不确定或有变化的可能,此时使用中介者模式可以降低变更引起的风险 扩散。
自己理解:中介者模式其实就是将原来的多个类里面的相互调用改成加一个中间类,通过中间类来相互调用,相当于数据库中的表之间的关系,由表关系多对多改成加一个中间表,中间表和其他的表关系是一对多。降低耦合性。
/**
* 例子:抽象同事类
* @author dell
*/
public abstract class AbstractColleague {
protected AbstractMediator medidtor;
public AbstractColleague(AbstractMediator _medidtor) {
this.medidtor = _medidtor;
}
}
/**
* 例子:同事类-采购管理
* @author dell
*/
public class Purchase extends AbstractColleague {
public Purchase(AbstractMediator _medidtor) {
super(_medidtor);
}
//采购IBM电脑
public void buyIBMcomputer(int number){
super.medidtor.execute("purchase.buy", number);
}
//不再采购IBM电脑
public void refuseBuyIBM(){
System.out.println("不再采购IBM电脑");
}
}
/**
* 例子:同事类-销售管理
* @author dell
*/
public class Sale extends AbstractColleague {
public Sale(AbstractMediator _medidtor) {
super(_medidtor);
}
//销售IBM电脑
public void sellIBMComputer(int number){
super.medidtor.execute("sale.sell", number);
System.out.println("销售IBM电脑"+number+"台");
}
//反馈销售情况,0~100变化,0代表根本没人买,100代表非常畅销,出一个卖一个
public int getSaleStatus(){
Random random = new Random(System.currentTimeMillis());
int saleStatus = random.nextInt(100);
System.out.println("IBM电脑的销售情况为:"+saleStatus);
return saleStatus;
}
//折价处理
public void offSale(){
super.medidtor.execute("sale.offsell");
}
}
/**
* 例子:同事类-库存管理
* @author dell
*/
public class Stock extends AbstractColleague {
public Stock(AbstractMediator _medidtor) {
super(_medidtor);
}
//刚开始有100台电脑
private static int COMPUTER_NUMBER = 100;
//库存增加
public void increase(int number){
COMPUTER_NUMBER = COMPUTER_NUMBER + number;
System.out.println("库存数量为:"+COMPUTER_NUMBER);
}
//库存降低
public void decrease(int number){
COMPUTER_NUMBER = COMPUTER_NUMBER-number;
System.out.println("库存数量为:"+COMPUTER_NUMBER);
}
//获得库存数量
public int getStockNumber(){
return COMPUTER_NUMBER;
}
//存货压力打了,就要通知采购人员不要采购,销售人员要尽快销售
public void clearStock(){
System.out.println("清理存货数量为:"+COMPUTER_NUMBER);
super.medidtor.execute("stock.clear");
}
}
/**
* 例子:抽象中介者
* @author dell
*/
public abstract class AbstractMediator {
protected Purchase purchase;
protected Sale sale;
protected Stock stock;
public AbstractMediator() {
purchase = new Purchase(this);
sale = new Sale(this);
stock = new Stock(this);
}
//中介者最重要的方法叫做事件方法,处理多个对象之间的关系
public abstract void execute(String str,Object...objects);
}
/**
* 例子:具体中介者
* @author dell
*/
public class Mediator extends AbstractMediator {
/**
* 中介者最重要的方法,用来具体调用同事类
*/
@Override
public void execute(String str, Object... objects) {
if(str.equals("purchase.buy")){//采购电脑
this.buyComputer((Integer)objects[0]);
}else if(str.equals("sale.sell")){//销售电脑
this.sellComputer((Integer)objects[0]);
}else if(str.equals("sale.offsell")){//折价销售
this.offSell();
}else if(str.equals("stock.clear")){//清仓处理
this.clearStock();
}
}
//采购电脑
private void buyComputer(int number){
int saleStatus = super.sale.getSaleStatus();
if(saleStatus>80){//销售情况良好
System.out.println("采购IBM电脑:"+number+"台");
super.stock.increase(number);
}else{
int buyNumber = number/2;
System.out.println("采购IBM电脑:"+buyNumber+"台");
}
}
//销售电脑
private void sellComputer(int number){
if(super.stock.getStockNumber()<number){//库存数量不够销售
super.purchase.buyIBMcomputer(number);
}
super.stock.decrease(number);
}
//折价销售电脑
private void offSell(){
System.out.println("折合销售IBM电脑"+stock.getStockNumber()+"台");
}
//清仓处理
private void clearStock(){
//要求清仓销售
super.sale.offSale();
//要求采购人员不要采购
super.purchase.refuseBuyIBM();
}
}
/**
* 场景类
* @author dell
*/
public class Client {
public static void main(String[] args) {
AbstractMediator mediator = new Mediator();
//采购人员采购电脑
System.out.println("---采购人员采购电脑----");
Purchase purchase = new Purchase(mediator);
purchase.buyIBMcomputer(100);
//销售人员销售电脑
System.out.println("---销售人员销售电脑----");
Sale sale = new Sale(mediator);
sale.sellIBMComputer(1);
//库房管理人员管理库存
System.out.println("---库房管理人员管理库存----");
Stock stock = new Stock(mediator);
stock.clearStock();
}
}
命令模式
定义:将一个请求封装成一个对象,从而让你使用不同的请求把客户端参数化。命令模式是一个高内聚的模式。
命令模式的单个角色:receive:接收者,command:命令角色,invoker:调用者角色
优点:
1.类间解耦:调用者角色与接收者角色之间没有任何依赖关系,调用者实现功能时只需要调用Command角色抽象类 execute方法就可以,不需要了解到底是哪个接收者执行。
2.可扩展性:command的子类可以非常容易的扩展,而调用者invoker和高层次的模块不产生严重的代码耦合。
3.命令模式可以结合责任链模式,实现命令族解析任务;结合模板方式,可以减少command子类的膨胀问题。
缺点:如果有N个命令,command的子类就会是N个,这个类会膨胀得非常大。
使用场景:只要是认为是命令的地方,都可以使用命令模式。
/**
* 抽象组
* @author dell
*/
public abstract class Group {
//甲乙双发分开办公,如果要和某个组讨论,首先要找到这个组。
public abstract void find();
//被要求增加功能
public abstract void add();
//被要求删除功能
public abstract void delete();
//被要求修改功能
public abstract void change();
//被要求给出所有的变更计划
public abstract void plan();
}
/**
* 美工组
* @author dell
*/
public class PageGroup extends Group {
@Override
public void find() {
System.out.println("找到美工组。。。");
}
@Override
public void add() {
System.out.println("客户要求增加一项需求。。。");
}
@Override
public void delete() {
System.out.println("客户要求删除一项需求。。。");
}
@Override
public void change() {
System.out.println("客户要求修改一项需求。。。");
}
@Override
public void plan() {
System.out.println("客户要求页面变更计划。。。");
}
}
/**
* 需求组
* @author dell
*/
public class RequirementGroup extends Group {
@Override
public void find() {
System.out.println("找到需求组。。。");
}
@Override
public void add() {
System.out.println("客户要求增加一项需求。。。");
}
@Override
public void delete() {
System.out.println("客户要求修改一项需求。。。");
}
@Override
public void change() {
System.out.println("客户要求删除一项需求。。。");
}
@Override
public void plan() {
System.out.println("客户要求需求变更计划。。。");
}
}
/**
* 代码组
* @author dell
*/
public class CodeGroup extends Group {
@Override
public void find() {
System.out.println("找到代码组。。。");
}
@Override
public void add() {
System.out.println("客户要求增加一项需求。。。");
}
@Override
public void delete() {
System.out.println("客户要求删除一项需求。。。");
}
@Override
public void change() {
System.out.println("客户要求修改一项需求。。。");
}
@Override
public void plan() {
System.out.println("客户要求代码变更计划。。。");
}
}
/**
* 抽象命令类
* @author dell
*/
public abstract class Command {
//把三个接收者都定义好,子类可以直接使用
protected RequirementGroup rg = new RequirementGroup();//需求组
protected PageGroup pg = new PageGroup();//美工组
protected CodeGroup cg = new CodeGroup();//代码组
//只有一个方法,让做什么事情
public abstract void execute();
}
/**
* 增加需求的命令
* @author dell
*/
public class AddRequirementCommand extends Command {
//执行增加一项需求的命令
@Override
public void execute() {
// 找到需求组
super.rg.find();
//增加一份需求
super.rg.add();
//给出计划
super.rg.plan();
}
}
/**
* 删除页面的命令
* @author dell
*/
public class DeletePageCommand extends Command {
//执行删除一个页面的命令
@Override
public void execute() {
//找到页面组
super.pg.find();
//删除一个页面
super.rg.delete();
//给出计划
super.rg.plan();
}
}
/**
* 负责人
* @author dell
*/
public class Invoker {
//什么命令
private Command command;
//客户发出命令
public void setCommand(Command command) {
this.command = command;
}
//执行客户的命令
public void action(){
this.command.execute();
}
}
/**
* 客户端
* @author dell
*/
public class Client {
//增加一项需求
// public static void main(String[] args) {
// //定义接头人(项目经理)
// Invoker lader = new Invoker();
// //客户要求增加一项需求,下命令
// Command command = new AddRequirementCommand();
// //接头人接到命令
// lader.setCommand(command);
// //接头人执行命令
// lader.action();
// }
//删除一个页面
public static void main(String[] args) {
//定义接头人(项目经理)
Invoker lader = new Invoker();
//客户要求增加一项需求--删除一个页面,下命令
Command command = new DeletePageCommand();
//接头人接到命令
lader.setCommand(command);
//接头人执行命令
lader.action();
}
}
责任链模式
定义:由一条链去处理相似的请求,在链中决定谁来处理这个请求,并返回相应的结果。
优点:将请求和处理分开,实现解耦。
缺点:
1.性能问题:每个请求都是从链头遍历到链尾,链比较长的时候,性能是一个非常大的问题。
2.调试不方便。
注意事项:链中的节数需要控制,避免出现超长链的情况(一般在Handler中设置一个最大的节点数量,在setNext方法中判断是否已经超过最大数量,超过则不允许该链建立)
/**
* 抽象处理者
* @author dell
*
*/
public abstract class Handler {
private Handler nextHandler;
//每个处理者都必须对请求做出处理
public final Response handlerMessage(Request request){
Response response = null;
//判断是否是自己的处理级别
if(this.getHandlerLevel().equals(request.getRequestLevel())){
response = this.echo(request);
}else{//不属于自己的处理级别
//判断是否是下一个处理者
if(this.nextHandler != null){
response = this.nextHandler.handlerMessage(request);
}else{
//没有适当的处理着,业务自行处理
}
}
return response;
}
//设置下一个处理者
public void setNext(Handler handler){
this.nextHandler=handler;
}
//每个处理者都有一个处理级别
protected abstract Level getHandlerLevel();
//每个处理者都必须实现处理任务
protected abstract Response echo(Request request);
}
/**
* 具体处理者
* @author dell
*
*/
public class ConcreteHandler1 extends Handler{
//定义自己的处理逻辑
@Override
protected Response echo(Request request) {
//完成处理逻辑
return null;
}
//设置自己的处理级别
@Override
protected Level getHandlerLevel() {
//设置自己的处理级别
return null;
}
}
/**
* 具体处理者
* @author dell
*
*/
public class ConcreteHandler2 extends Handler{
//定义自己的处理逻辑
@Override
protected Response echo(Request request) {
//完成处理逻辑
return null;
}
//设置自己的处理级别
@Override
protected Level getHandlerLevel() {
//设置自己的处理级别
return null;
}
}
/**
* 具体处理者
* @author dell
*
*/
public class ConcreteHandler3 extends Handler{
//定义自己的处理逻辑
@Override
protected Response echo(Request request) {
//完成处理逻辑
return null;
}
//设置自己的处理级别
@Override
protected Level getHandlerLevel() {
//设置自己的处理级别
return null;
}
}
/**
* 模式中有关框架代码
* @author dell
*
*/
public class Level {
//定义一个请求和处理级别
}
/**
* 模式中有关框架代码
* @author dell
*/
public class Request {
//请求的等级
public Level getRequestLevel(){
return null;
}
}
/**
* 模式中有关框架代码
* @author dell
*/
public class Response {
//处理者返回的数据
}
/**
* 通用 - 场景类
* @author dell
*
*/
public class Client {
public static void main(String[] args) {
//声明所有的处理节点
Handler handler1=new ConcreteHandler1();
Handler handler2=new ConcreteHandler2();
Handler handler3=new ConcreteHandler3();
//设置链中的阶段顺序1-->2-->3
handler1.setNext(handler2);
handler2.setNext(handler3);
//提交请求,返回结果
Response response = handler1.handlerMessage(new Request());;
}
}
装饰模式
定义:动态的给对象添加一些额外的职责(就增加功能来说,装饰模式相比生成子类更灵活).
角色:Component抽象构件、ConcreteComponent具体构件、Decorator装饰角色、具体装饰角色
优点:
1.装饰类和被装饰类可以独立发展,不会相互耦合。
2.装饰模式是继承关系的一个替代方案。
3.装饰模式可以动态的扩展一个实现类的功能。
4.装饰模式可以替代继承,解决类膨胀的问题(继承是静态的给类增加功能,装饰模式是动态的增加功能)。
缺点:多层装饰比较复杂(工作量大)。应尽量减少装饰类的数量,以便降低系统的复杂度。
使用场景:
1.需要扩展一个类的功能,或给一个类增加附加功能。
2.需要动态的给一个对象增加功能,这些功能可以再动态的撤销。
3.需要动态的为一批兄弟类进项改装或加装功能。
/**
* 通用-抽象构件
* @author dell
*/
public abstract class Component {
//抽象的方法
public abstract void operate();
}
/**
* 通用-具体构件
* @author dell
*/
public class ConcreteComponent extends Component {
//具体实现
@Override
public void operate() {
System.out.println("do Something");
}
}
/**
* 通用-抽象装饰者
* @author dell
*/
public class Decorator extends Component {
private Component component = null;
//通过构造函数传递被装饰者
public Decorator(Component component) {
this.component = component;
}
//委托给被修饰者执行
@Override
public void operate() {
this.component.operate();
}
}
/**
* 通用-具体的装饰类
* @author dell
*/
public class ConcreteDecorator1 extends Decorator {
//定义被修饰者
public ConcreteDecorator1(Component component) {
super(component);
}
//定义自己的修饰方法
private void method1(){
System.out.println("method1 ...");
}
//重新父类的operate方法:原始方法和装饰方法的执行顺序在具体的装饰类中是固定的,可以通过方法重载实现多种执行顺序
public void operate(){
this.method1();
super.operate();
}
}
/**
* 通用-具体的修饰类
* @author dell
*/
public class ConcreteDecorator2 extends Decorator {
//定义被修饰者
public ConcreteDecorator2(Component component) {
super(component);
}
//定义自己的修饰方法
private void method2(){
System.out.println("method2 ...");
}
//重新父类的operate方法:原始方法和装饰方法的执行顺序在具体的装饰类中是固定的,可以通过方法重载实现多种执行顺序
public void operate(){
super.operate();
this.method2();
}
}
/**
* 通用-场景类
* @author dell
*/
public class Client {
public static void main(String[] args) {
//被修饰类
Component component = new ConcreteComponent();
//第一次修饰
component = new ConcreteDecorator1(component);
//第二次修饰
component = new ConcreteDecorator2(component);
//修饰后运行
component.operate();
}
}
策略模式
定义:定义一组算法,将每个算法都封装起来,并且使他们之间可以互换。
角色:Context封装角色、Strategy抽象策略角色、ConcreteStrategy具体策略角色
优点:
1.算法可以自由切换
2.避免使用多重条件判断
3.扩展性良好
缺点:
1.策略类数量增多
2.所有的策略类都需要对外暴露(可以与工厂方法、代理模式或享元模式等结合)
使用场景:
1.多个类只有在算法或行为上稍有不同的场景
2.算法需要自由切换的场景
3.需要屏蔽算法规则的场景
注意事项:如果系统中的一个策略家族的具体策略数量超过4个,则需要考虑使用混合模式,解决策略类膨胀和对外暴露的问题。
自己理解:策略模式使用的就是面向对象的继承和多态机制。它的重点是封装角色,和代理模式的差别就是策略模式的封装角色和被封装的策略类不用是同一个接口,如果是同一个接口,那就是代理模式。策略模式在项目中使用的非常多,但单独使用的地方少,因为它所有的策略都需要暴露出去。在实际项目中,一般通过工厂方法模式来实现策略类的声明。
策略模式扩展:策略枚举
定义:是一个枚举 且 是一个浓缩了的策略模式的枚举。
使用场景:策略枚举是一个非常优秀和方便的模式,但是她受枚举类型的限制,每个枚举项都是public、static、final的,扩展性受到约束。所以,策略枚举一般担当不经常发生变化的角色。
/**
* 通用-抽象的策略角色
* @author dell
*
*/
public interface Strategy {
//策略模式的运算法则
public void doSomething();
}
/**
* 通用-封装角色
* @author dell
*
*/
public class Context {
//抽象策略
private Strategy strategy = null;
//构造函数设置具体策略
public Context(Strategy strategy){
this.strategy=strategy;
}
//封装后的策略方法
public void doAnything(){
this.strategy.doSomething();
}
}
/**
* 通用-具体策略角色
* @author dell
*
*/
public class ConcreteStrategy1 implements Strategy{
@Override
public void doSomething() {
System.out.println("具体策略1的运算法则");
}
}
/**
* 通用-具体的策略角色
* @author dell
*
*/
public class ConcreteStrategy2 implements Strategy {
@Override
public void doSomething() {
System.out.println("具体策略2的运算法则");
}
}
/**
* 通用-场景类
* @author dell
*
*/
public class Client {
public static void main(String[] args) {
//声明一个具体的策略
Strategy strategy = new ConcreteStrategy1();
//声明上下文对象
Context context = new Context(strategy);
//执行封装后的方法
context.doAnything();
}
}
适配器模式
定义:将一个类的接口变换成客户端所期待的零一种接口,从而使原本因接口不匹配而无法在一起工作的两个类能够在一起工作。
角色:目标角色、源角色、适配器角色
优点:
1.适配器模式可以让两个没有任何关系的类在一起运行。
2.增加了类的透明性:访问目标角色,但具体的实现都委托给了源角色,这些对高层次模块都是透明的。
3.提高了类的复用度。
4.灵活性好:新需求扩展时可以加适配器,不需要时可以删掉。
使用场景:想修改一个已经运行的接口时,可以使用适配器模式。
注意事项:
1.适配器模式可以解决已经运行的项目的问题,项目还在开发阶段不需要使用适配器模式,直接改接口就行了。
2.项目一定要遵守依赖倒置原则和里式替换原则,否则即使 使用了适配器模式,也会带来非常大的改造。
对象适配器:通过对象层次的关联关系进行委托,而不是继承关系。
类适配器:通过继承关系进行适配。
对象适配器和类适配器的区别:类适配器是类间继承,对象适配器是对象的合成关系,也可以说是类的关联关系。
自己理解:适配器模式就是将两个不同的接口结合起来,比如A、B两个接口,想通过A接口获取B接口的返回结果,无法直接调用,而且已经投入运行的项目的接口不会随意修改,这时候可以使用适配器模式,重新建一个接口C,将两个接口结合起来,通过调用接口C获取想要的效果。
/**
* 通用-目标角色
* @author dell
*/
public interface Targer {
//目标角色有自己的方法
public void request();
}
/**
* 通用-目标角色的实现类
* @author dell
*/
public class ConcreteTarger implements Targer {
@Override
public void request() {
System.out.println("if you need any help,pls call me !");
}
}
/**
* 通用-源角色
* @author dell
*/
public class Adaptee {
//原有的业务逻辑
public void doSomething(){
System.out.println("I'm kind of busy,leave me ale=one,pls!");
}
}
/**
* 通用-适配器角色
* @author dell
*/
public class Adapter extends Adaptee implements Targer {
@Override
public void request() {
super.doSomething();
}
}
/**
* 通用-场景类
* @author dell
*/
public class Client {
public static void main(String[] args) {
//原有的业务逻辑
Targer targer = new ConcreteTarger();
targer.request();
//增加了适配器后的业务逻辑
Targer targer2 = new Adapter();
targer2.request();
}
}
迭代器模式
定义:提供一个方法访问一个 容器对象 中各个元素,而又不需要暴露该对象的内部细节。
角色:Iterator抽象迭代器、具体迭代器、抽象容器、具体容器
实践:迭代器目前已经是一个没落的模式,基本上没人会单独写一个迭代器,使用Java提供的Iterator一般就能满足要求了。
自己理解:迭代器模式就是一个遍历的方法。提供一个方法,遍历一个容器里面的数据。因为Java已经提供了相应的遍历接口,所以如果没有特殊的要求,不需要自己写跌打器,直接使用java自带的方法遍历。
/**
* 通用--抽象容器
* @author dell
*/
public interface Aggregate {
//是容器必然有元素的添加
public void add(Object object);
//减少元素
public void remove(Object object);
//由迭代器遍历所有的元素
public Iterator iterator();
}
/**
* 通用--具体容器
* @author dell
*/
public class ConcreteAggregate implements Aggregate{
//容纳对象的容器
private Vector vector = new Vector();
@Override
public void add(Object object) {
this.vector.add(object);
}
@Override
public void remove(Object object) {
this.remove(object);
}
@Override
public Iterator iterator() {
return new ConcreteIterator(this.vector);
}
}
/**
* 通用--抽象迭代器
* @author dell
*/
public interface Iterator {
//遍历到下一个元素
public Object next();
//是否已经遍历到尾部
public boolean hasNext();
//删除当前指向的元素
public boolean remove();
}
/**
* 通用--具体迭代器
* @author dell
*/
public class ConcreteIterator implements Iterator {
//容器
private Vector vector = new Vector();
//定义当前游标
public int cursor = 0;
public ConcreteIterator(Vector vector) {
this.vector = vector;
}
@Override
public Object next() {
Object result = null;
if(this.hasNext()){
result = this.vector.get(this.cursor++);
}else{
return null;
}
return result;
}
@Override
public boolean hasNext() {
if(this.cursor == this.vector.size()){
return false;
}else{
return true;
}
}
@Override
public boolean remove() {
this.vector.remove(this.cursor);
return true;
}
}
/**
* 通用--场景类
* @author dell
*/
public class Client {
public static void main(String[] args) {
//声明出容器
Aggregate agg = new ConcreteAggregate();
//产生对象数据放进去
agg.add("abc");
agg.add("aaa");
agg.add("123");
//遍历一下
Iterator iterator = agg.iterator();
while(iterator.hasNext()){
System.out.println(iterator.next());
}
}
}
组合模式
定义:将对象组合成树形结构以表示"部分-整体"的层次结构,使得用户对单个对象和组合对象的使用具有一致性(主要是用来描述部分与整体的关系)。
角色:抽象构件角色、叶子构件、树枝构件
优点:
1.高层模块调用简单
2.节点自由增加
缺点:树叶和树枝类直接使用了实现类,与依赖倒置原则冲突。
使用场景:
1.维护和展示 部分-整体关系的场景,如树形菜单、文件和文件夹管理。
2.从一个整体中能够独立出部分模块或功能的场景。
组合模式的两种实现:安全的组合模式和透明的组合模式
安全模式:在数据库中定义 父节点(代码中不需要体现组合模式)。
透明模式:把用来组合使用的方法放到抽象类中。透明模式的好处是遵循了依赖倒置原则。
自己理解:组合模式就是一个整体的对象中包含许多单独的对象,即许多小对象组合成了一个大对象,小对象中可能包含更小的对象,例如树形结构的样式(或文件夹的样式)。组合模式一共有两种实现方式,一种是安全模式,即在数据库的表中定义父节点字段,通过表字段判断层级关系。另一种方式是透明模式,即在组合模式的基础上,将组合涉及到的方法(添加、删除、修改节点等方法)放到抽象类中。这种方式是为了解决组合模式中与依赖倒置原则冲突的问题。组合模式在树形结构、文件夹结构等涉及到层级关系的场景中使用。优点是高层模块操作节点时比较简单且操作节点的灵活性高。
/**
* 通用-抽象构件
* @author dell
*
*/
public abstract class Component {
//个体和整体够具有的共享
public void doSomething(){
//编写业务逻辑
}
}
/**
* 树枝构件
* @author dell
*/
public class Composite extends Component {
//构件容器
private List<Component> componentList = new ArrayList<>();
//增加一个叶子构件或树枝构件
public void add(Component component){
this.componentList.add(component);
}
//删除一个叶子构件或树枝构件
public void remove(Component component){
this.componentList.remove(component);
}
//获得分之下的所有叶子构件和树枝构件
public List<Component> getChildren(){
return this.componentList;
}
}
/**
* 树叶构件
* @author dell
*/
public class Leaf extends Component {
/**
* 可以复写父类方法
* public void doSomethind(){}
*
*/
}
/**
* 通用-场景类
* @author dell
*/
public class Client {
public static void main(String[] args) {
//创建一个根节点
Composite root = new Composite();
root.doSomething();
//创建一个树枝构件
Composite branch = new Composite();
//创建一个叶子节点
Leaf leaf = new Leaf();
//建立整体
root.add(branch);
branch.add(leaf);
}
//通过递归遍历树
public static void display(Composite root){
for (Component c : root.getChildren()) {
if(c instanceof Leaf){//叶子节点
c.doSomething();
}else{//树枝节点
display((Composite)c);
}
}
}
}
观察者模式
定义:观察者模式也叫发布订阅模式,定义对象间一种一对多的依赖关系,使得每当一个对象改变状态,则所有依赖于它的对象都会得到通知并自动更新。
角色:被观察者、观察者、具体呗观察者、具体观察者
优点:
1.观察者和被观察者之间是抽象耦合。
2.建立了一套触发机制。
缺点:观察者模式需要考虑一下开发效率和运行效率问题。java中消息的通知默认是顺序执行,一个观察者卡壳,会影响整体的运行效率。
使用场景:
1.关联行为场景。注意:关联行为是可拆分的,而不是’组合’关系。
2.事件多级触发场景。
3.跨系统的消息交换场景,如消息队列的处理机制。
注意事项:
1.广播链的问题:建议,在一个观察者模式中最多出现一个对象既是观察者也是被观察者,也就是说消息最多转发一次(传递两次)。
2.异步处理问题(可以使用多线程或缓存技术)。
自己理解:观察者模式即发布订阅模式,一个动作完成后自动启动另一个动作。
/**
* 通用-观察者
* @author dell
*/
public interface Observer {
//更新方法
public void update();
}
/**
* 通用-具体观察者
*/
public class ConcreteObserver implements Observer {
@Override
public void update() {
System.out.println("接收到信息,并进行处理!");
}
}
/**
* 通用-被观察者
* @author dell
*/
public abstract class Subject {
//定义一个观察者数组
private Vector<Observer> obsVector = new Vector<>();
//增加一个观察者
public void addObserver(Observer o){
this.obsVector.add(o);
}
//删除一个观察者
public void delObserver(Observer o){
this.obsVector.remove(o);
}
//通知所有观察者
public void notifyObservers(){
for (Observer o : this.obsVector) {
o.update();
}
}
}
/**
* 通用-具体被观察者
* @author dell
*/
public class ConcreteSubject extends Subject {
//具体的业务
public void doSomething(){
super.notifyObservers();
}
}
/**
* 通用-场景类
* @author dell
*/
public class Client {
public static void main(String[] args) {
//创建一个被观察者
ConcreteSubject subject = new ConcreteSubject();
//定义一个观察者
Observer obs = new ConcreteObserver();
//观察者 观察 被观察者
subject.addObserver(obs);
//观察者开始活动了
subject.doSomething();
}
}
门面模式:
定义:也叫外观模式,要求一个子系统的外部与其内部的通信必须通过一个统一的对象进行。门面模式提供一个高层次的接口,使得子系统易于使用。即:门面对象是外界访问子系统内部的唯一通道。
角色:门面模式、子系统角色(子系统是类的集合)。
优点:
1.减少系统的相互依赖。
2.提高了灵活性。
3.提高安全性。
缺点:不符合开闭原则,对修改关闭,对扩展开放。
使用场景:
1.为一个复杂的模块或子系统提供一个供外界访问的接口。
2.子系统相对独立,外界对子系统的访问只要黑箱操作即可。
3.预防低水平开发人员带来的风险扩散。
注意事项:
1.子系统可以有多个门面(一般一个就够了)。
2.门面不参与子系统内的业务逻辑。
自己理解:门面模式是一个封装方法,将一些相关的业务逻辑封装到几个类中(一个子系统),然后提供一个统一的访问路径,使外界只需要通过方法即可访问该子系统,子系统的内部实现及以后的修改,都对外界无关。类似于开发中处理接收到的http请求的那个类就是门面模式。
/**
* 例子-写信过程接口
* @author dell
*/
public interface ILetterProcess {
//首先要写信的内容
public void writeContext(String context);
//其次写信封
public void fillEnvelope(String address);
//把信放到信封里
public void letterInotoEnvelope();
//然后邮递
public void sendLetter();
}
/**
* 例子-写信过程的实现
* @author dell
*/
public class LetterProcess implements ILetterProcess {
@Override
public void writeContext(String context) {
System.out.println("填写信的内容:"+context);
}
@Override
public void fillEnvelope(String address) {
System.out.println("填写收件人信息:"+address);
}
@Override
public void letterInotoEnvelope() {
System.out.println("把信放到信封中");
}
@Override
public void sendLetter() {
System.out.println("邮递信件");
}
}
/**
* 例子-信件检查类
* @author dell
*/
public class Police {
//警察要在信件邮寄前检查信件
public void checkLetter(ILetterProcess letterProcess){
System.out.println(letterProcess+"信件已经检查过了");
}
}
/**
* 例子-邮局
* @author dell
*/
public class ModenPostOffice {
private ILetterProcess letterProcess = new LetterProcess();
private Police police = new Police();
//写信、封装、检查、投递 一体化
public void sendLetter(String context,String address){
//帮忙写信
letterProcess.writeContext(context);
//写好信封
letterProcess.fillEnvelope(address);
//警察检查信件
police.checkLetter(letterProcess);
//把信放到信封中
letterProcess.letterInotoEnvelope();
//邮递信件
letterProcess.sendLetter();
}
}
/**
* 例子-场景类
* @author dell
*/
public class Client {
public static void main(String[] args) {
//邮局
ModenPostOffice modenPostOffice = new ModenPostOffice();
//你只要把信的内容和收信人信息给它,它会帮你完成一系列工作
//定义一个地址
String address = "地球村**********";
//信的内容
String context = "我在学习门面模式...";
//邮局帮忙发出去
modenPostOffice.sendLetter(context, address);
}
}
备忘录模式
定义:在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到原先保存的状态。
角色:发起人角色、备忘录角色、备忘录管理员角色
使用场景:
1.需要保存和恢复数据的相关状态场景。
2.提供一个可回滚的操作。
3.需要监控的副本场景中。
4.数据库连接的事物管理就是用的备忘录模式。
注意事项:
1.备忘录的生命周期:建立后就要使用,不使用就要立刻删除其引用,使其等待垃圾回收器处理。
2.备忘录的性能:不要在频繁建立备份的场景中使用备忘录模式(无法控制数量、大对象的建立需要消耗资源,影响性能)。
自己理解:备忘录模式就是保存了某个时刻的数据,等需要的时候通过备忘录获取当时的数据 将此时的数据进行覆盖。可以备份单个属性、对象、集合、多个属性、多个对象等等。可以通过set/get方法、clone()、反射等方式进行备份。总之,需要根据具体的场景选择不同的方式进行备份。备忘录的建立、销毁也是消耗性能的,所以需要控制建立的数量和声明周期。适用于需要保存、恢复数据、可回滚操作、建立副本等需要保存某一时刻数据的场景。
/**
* 通过-发起人角色
* @author dell
*/
public class Originator {
//内部状态
private String state = "";
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
//创建一个备忘录
public Memento createMemento(){
return new Memento(this.state);
}
//恢复一个备忘录
public void restoreMemento(Memento memento){
this.setState(memento.getState());
}
}
/**
* 通用-备忘录角色
* @author dell
*
*/
public class Memento {
//发起人的内部状态
private String state ="";
//构造函数传递参数
public Memento(String state) {
this.state = state;
}
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
}
/**
* 通用-备忘录管理员角色
* @author dell
*
*/
public class Catetaker {
//备忘录对象
private Memento memento;
public Memento getMemento() {
return memento;
}
public void setMemento(Memento memento) {
this.memento = memento;
}
}
/**
* 通用-场景类
* @author dell
*
*/
public class Client {
public static void main(String[] args) {
//定义发起人
Originator originator = new Originator();
//定义备忘录管理员
Catetaker catetaker = new Catetaker();
//创建一个备忘录
catetaker.setMemento(originator.createMemento());
//恢复一个备忘录
originator.restoreMemento(catetaker.getMemento());
}
}
访问者模式
定义:封装一些作用于某种数据结构中的各元素的操作,它可以在不改变数据结构的前提下定义作用于这些元素的新操作。
角色:抽象访问者、具体访问者、抽象元素、具体元素(一般有几个具体元素就有几个访问方法)、结构对象。
优点:
1.符合单一职责原则。
2.优秀的扩展性。
3.灵活性高。
缺点:
1.具体元素对访问者公布细节。
2.具体元素变更比价困难。
3.违背了依赖倒置原则。
使用场景:业务规则要求遍历多个不同的对象。
自己理解:访问者模式将任务集中化,即将有关行为集中到一个访问者对象中,其改变不影响系统数据结构。访问者模式就是一种分离对象数据结构与行为的方法,通过这种分离,可达到为一个被访问者动态添加新的操作而无需做其它的修改的效果。
/**
* 通用-抽象元素
* @author dell
*
*/
public abstract class Element {
//定义业务逻辑
public abstract void doSomething();
//允许谁来访问
public abstract void accept(IVisitor visitor);
}
/**
* 通用-具体元素
* @author dell
*
*/
public class ConcreteElement1 extends Element{
@Override
public void doSomething() {
// 业务处理
}
@Override
public void accept(IVisitor visitor) {
// 允许哪个访问者访问
visitor.visit(this);
}
}
/**
* 通用-具体元素
* @author dell
*
*/
public class ConcreteElement2 extends Element{
@Override
public void doSomething() {
// 业务处理
}
@Override
public void accept(IVisitor visitor) {
// 允许哪个访问者访问
visitor.visit(this);
}
}
/**
* 通用-抽象访问者
* @author dell
*
*/
public interface IVisitor {
//可以访问哪些对象
public void visit(ConcreteElement1 el1);
public void visit(ConcreteElement2 el2);
}
/**
* 通用-具体访问者
* @author dell
*
*/
public class Visitor implements IVisitor {
// 访问el1元素
@Override
public void visit(ConcreteElement1 el1) {
el1.doSomething();
}
// 访问el1元素
@Override
public void visit(ConcreteElement2 el2) {
el2.doSomething();
}
}
/**
* 通用-结构对象
* @author dell
*
*/
public class ObjectStruture {
//对象生成器,这里通过一个工厂方法模拟
public static Element createElement(){
Random random = new Random();
if(random.nextInt(100)>50){
return new ConcreteElement1();
}else{
return new ConcreteElement2();
}
}
}
/**
* 通用-场景类
* @author dell
*
*/
public class Client {
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
//获得元素对象
Element element = ObjectStruture.createElement();
//接受访问者访问
element.accept(new Visitor());
}
}
}
状态模式
定义:当一个对象内在状态改变时允许其改变行为,这个对象看起来像改变了其类。
核心:状态模式的核心是封装,状态的变更引起了行为的变更,从外部看好像这个对象对应的类发生了改变。
角色:抽象状态角色、具体状态角色、环境角色
优点:
1.结构清晰:避免了过多的if-else或switch-case的使用,避免了程序的复杂性。
2.遵循设计原则:体现了开闭原则和单一职责原则,每个状态都是一个子类,增加或修改状态,需要增加或修改子类。
3.封装性好:将状态变换放置到类的内部实现。
缺点:子类会太多,类膨胀。
使用场景:
1.行为随状态改变的场景。
2.条件、分支判断语句的替代者。
注意事项:状态模式适用于当某个对象在它的状态发生改变时,它的行为也随着发生比较大的变化,也就是说在行为受状态约束的情况下可以使用状态模式,而且使用时对象的状态最好不要超过5个。
自己理解:状态模式就是将行为进行封装,什么情况下产生什么行为,不需要使用if-else等判断语句进行判断,而是定义一些状态(每个状态是一个子类),通过执行这些状态来调用行为(子类里面规定了该状态下可以有哪些操作)。
/**
* 通用-抽象环境角色
* @author dell
*/
public abstract class State {
//定义一个环境角色,提供子类访问
protected Context context;
//设置环境角色
public void setContext(Context context) {
this.context = context;
}
//行为1
public abstract void handle1();
//行为2
public abstract void handle2();
}
/**
* 通用-环境角色
* @author dell
*/
public class ConcreteState1 extends State {
@Override
public void handle1() {
// 本状态下必须处理的逻辑
}
@Override
public void handle2() {
// 设置当前状态为Stat2
super.context.setCurrentState(Context.STATE2);
//过渡到state2状态,由Context实现
super.context.handle2();
}
}
/**
* 通用-环境角色
* @author dell
*/
public class ConcreteState2 extends State {
@Override
public void handle1() {
// 设置当前状态为Stat1
super.context.setCurrentState(Context.STATE1);
//过渡到state1状态,由Context实现
super.context.handle1();
}
@Override
public void handle2() {
// 本状态下必须处理的逻辑
}
}
/**
* 通用-具体环境角色
* @author dell
*
* 环境角色有两个不成文的约定:
* 1.把状态对象声明为静态常量,有几个状态对象就声明几个静态常量。
* 2.环境角色具有状态抽象角色定义的所有行为,具体执行使用委托方式。
*/
public class Context {
//定义状态
public final static State STATE1 = new ConcreteState1();
public final static State STATE2 = new ConcreteState2();
//当前状态
private State CurrentState;
//获得当前状态
public State getCurrentState(){
return CurrentState;
}
//设置当前状态
public void setCurrentState(State currentState){
this.CurrentState=currentState;
//切换状态
this.CurrentState.setContext(this);
}
//行为委托
public void handle1(){
this.CurrentState.handle1();
}
public void handle2(){
this.CurrentState.handle2();
}
}
/**
* 通用-具体环境角色
* @author dell
*/
public class Client {
public static void main(String[] args) {
//定义环境角色
Context context = new Context();
//初始化状态
context.setCurrentState(new ConcreteState1());
//行为执行
context.handle1();
context.handle2();
}
}
解释器模式
定义:给定一门语言,定义它的文法的一种表示,并定义一个解释器,该解释器使用该表示来解释语言中的句子。解析器是一个语法解析工具。
角色:抽象解释器、终结符表达式、非终结符表达式、环境角色
优点:扩展性好,修改语法规则只需要修改相应的非终结符表达式就可以,如果扩展语法,则只要增加非终结符类。
缺点:
1.引起类膨胀:每个语法都会产生非终结符表达式,维护麻烦。
2.采用递归调用方法,会导致调试复杂。
3.效率问题(使用了大量的循环和递归)。
使用场景:
1.重复发生的问题可以使用解释器(例如解析每天的日志)
2.一个简单语法需要解释的场景(语法越复杂,复杂度越高,越不适用解释器)。
注意事项:尽量不要在重要的模块使用解释器(不方便维护),可以使用脚本语言代替。
自己理解:解释器模式就是定义一个语法规则,然后定义一个解释器,通过该解释器解析规则得到相应的结果。但解释器使用场景少,且使用脚本语言可以达到解释器的功能,而且比解释器简单,也有好多定义好的解释器可以使用。所以,尽量不要自己写解释器。正则表达式就是一个解释器。
享元模式:
定义:使用共享对象可有效地支持大量的细粒度的对象。
角色:抽象享元角色、具体享元角色、不可共享的享元角色、享元工厂
优点:减少应用程序创建的对象,降低程序内存的占用,增强程序的性能。
缺点:提高系统的复杂性。
使用场景:
1.系统中存在大量的相似对象。
2.细粒度的对象都具备较接近的外部状态,而且与内部状态无关,即对象没有特定身份。
3.需要缓冲池的场景。
自己理解:享元模式即使用共享技术,使得一些细粒度的对象可以共享.将一些有共性的对象抽成一个对象,不同的属性在调用对象时赋值进去,这样可以减少对象的数量,降低内存的占用。类似于建立一个池,但需要考虑并发的问题。享元模式和对象池还是有差别的,对象池重点在对象的复用上,池中的每个对象都可以替换。享元模式主要解决对象的共享问题,如何建立多个可共享的细粒度对象则是其关注的重点。
/**
* 通用-抽象享元角色
* @author dell
*/
public abstract class Flyweight {
//内部状态:共有的属性
private String intrinsic;
//外部状态 final防止外部状态修改
protected final String Extrinsic;
//要求享元角色必须接受外部状态
public Flyweight(String extrinsic) {
this.Extrinsic = extrinsic;
}
//定义业务操作
public abstract void operate();
public String getIntrinsic() {
return intrinsic;
}
public void setIntrinsic(String intrinsic) {
this.intrinsic = intrinsic;
}
}
/**
* 通用-具体享元角色
* @author dell
*/
public class ConcreteFlyweight1 extends Flyweight {
public ConcreteFlyweight1(String extrinsic) {
super(extrinsic);
}
//根据外部状态进行逻辑处理
@Override
public void operate() {
// 业务逻辑
}
}
/**
* 通用-具体享元角色
* @author dell
*
*/
public class ConcreteFlyweight2 extends Flyweight {
public ConcreteFlyweight2(String extrinsic) {
super(extrinsic);
}
//接受外部状态
@Override
public void operate() {
// 业务逻辑
}
}
/**
* 通用-享元工厂
* @author dell
*
*/
public class FlyweightFactory {
//定义一个池容器
private static Map<String,Flyweight> pool = new HashMap<String,Flyweight>();
//享元工厂
public static Flyweight getFlyweight(String extrinsic){
//需要返回的对象
Flyweight flyweight = null;
//在池中没有该对象
if(pool.containsKey(extrinsic)){
flyweight = pool.get(extrinsic);
}else{
//根据外部状态创建享元对象
flyweight = new ConcreteFlyweight1(extrinsic);
//放置到池中
pool.put(extrinsic, flyweight);
}
return flyweight;
}
}
/**
* 通用-场景类
* @author dell
*/
public class Client {
public static void main(String[] args) {
Flyweight flyweight = FlyweightFactory.getFlyweight("key");
flyweight.operate();
}
}
桥梁模式:
定义:将抽象和实现解耦,使得两者可以独立地变化。
角色:抽象化角色、实现化角色、修正抽象化角色、具体实现化角色。即抽象角色引用实现角色或者说抽象角色的部分实现是由实现角色完成的。
优点:
1.抽象和实现分离:解决继承的缺点。
2.优秀的扩充能力。
3.实现细节对客户透明。
使用场景:
1.不希望或不使用使用继承的场景。
2.接口或抽象类不稳定的场景。
3.重用性要求较高的场景。
自己理解:桥梁模式即将抽象与实现分开,可以解决继承的缺点,主要是实现解耦。
/**
* 通用-实现化角色
* @author dell
*/
public interface Implementor {
//基本方法
public void doSomething();
public void doAnything();
}
/**
* 通用-具体实现化角色
* @author dell
*
*/
public class ConcreteImplementor1 implements Implementor {
@Override
public void doSomething() {
// 业务逻辑处理
}
@Override
public void doAnything() {
// 业务逻辑处理
}
}
/**
* 通用-具体实现化角色
* @author dell
*
*/
public class ConcreteImplementor2 implements Implementor {
@Override
public void doSomething() {
// 业务逻辑处理
}
@Override
public void doAnything() {
// 业务逻辑处理
}
}
/**
* 通用-抽象化角色
* @author dell
*
*/
public abstract class Abstraction {
//定义对实现化角色的引用
private Implementor imp;
//约束子类必须实现该构造函数
public Abstraction(Implementor imp) {
this.imp = imp;
}
//自身的行为和属性
public void request(){
this.imp.doSomething();
}
//获得实现化角色
public Implementor getImp(){
return imp;
}
}
/**
* 具体抽象化角色
* @author dell
*
*/
public class RefinedAbstraction extends Abstraction {
public RefinedAbstraction(Implementor imp) {
super(imp);
}
//修正父类的行为
public void request(){
//业务处理
super.request();
super.getImp().doAnything();
}
}
/**
* 通用-场景类
* @author dell
*
*/
public class Client {
public static void main(String[] args) {
//定义一个实现化角色
Implementor imp = new ConcreteImplementor1();
//定义一个抽象化角色
Abstraction abstraction = new RefinedAbstraction(imp);
//执行行文
abstraction.request();
}
}
以上是23种设计模式。