Java 6大设计原则28大设计模式

6大设计原则:

1. 单一职责原则

单一职责原则的英文名称是 Single Responsibility Principle ,简称是SRP(There should never be more than one reason for a class to change.)
见名知意,充斥着满满的争议,需求的更替,版本的迭代,场景的复杂,种种都能导致这个被破坏,但是把出发点是好的,单一意味着简单,简单意味着易懂,而后易维护、节省人力、成本、增加稳定性;但是场景复杂时就类就多了,em...;仁者见仁智者见智,不展开讲了

2. 里氏替换原则

里氏替换原则( Liskov Substitution Principle, LSP
定义1:
If for each object o1 of type S there is an object o2 of
type T such that for all programs P defined in terms of T,the behavior of P is unchanged when o1 is
substituted for o2 then S is a subtype of T. (如果对每一个类型为 S 的对象 o1 ,都有类型为 T 的对
o2 ,使得以 T 定义的所有程序 P 在所有的对象 o1 都代换成 o2 时,程序 P 的行为没有发生变
化,那么类型 S 是类型 T 的子类型。)
定义2:
Functions that use pointers or references to base classes must be able to use
objects of derived classes without knowing it. (所有引用基类的地方必须能透明地使用其子类的
对象。)
说的就是Java中父子关系(继承、实现),父类出现的地方,可替换成子类
原则上相当于制定了契约
正例不举了,我举个反例吧:
如果其他程序调用时,不能通过父类或者接口,而必须使用子类时,则说明类的设计已经违背了LSP原则;
public interface A {
    void method();
}

public class AImpl implements A {
    public void method() {
    }
    public void methodSub() {
    }
}

// 使用方

public class Test {
    public static void main(String[] args) {
        AImpl obj = new AImpl();
        // 外部调用时obj对象的类不能替换成A,则违反了LSP
        obj.methodSub();
    }
}

3. 依赖倒置原则

依赖倒置原则( Dependence Inversion Principle,DIP
High level modules should not depend upon low level modules.Both should depend upon
abstractions.Abstractions should not depend upon details.Details should depend upon abstractions.
  • 模块间的依赖通过抽象发生(接口或抽象类)
  • 接口或抽象类不依赖于实现类
  • 实现类依赖接口或抽象类
简而言之就是解耦

4. 接口隔离原则

Interface Segregation Principle

  • Clients should not be forced to depend upon interfaces that they don't use. (客户端不应该依
    赖它不需要的接口。)
  • The dependency of one class to another one should depend on the smallest possible interface. (类间的依赖关系应该建立在最小的接口上。)
简而言之,用什么依赖什么
所以对接口就要求
  • 接口职能单一,服务定制,设计有度
  • 接口要高内聚,减少交互

5. 迪米特法则

迪米特法则(Law of Demeter,LoD)也称为最少知识原则(Least Knowledge Principle,LKP)
只通过接口协定交互,其他一概不管;Only talk to your immediate friends(只与直接的朋友沟通。)
是自己的逻辑就是自己的逻辑,不是自己的就不是自己的

6. 开闭原则

Open Closed Principle
Software entities like classes,modules and functions should be open for extension but closed for
modifications. (一个软件实体如类、模块和函数应该对扩展开放,对修改关闭。)
开闭原则个人粗浅的见解是一个法典,像一种理想化,一种指导方针;我们需要通过抽象约束、规范制度、需求预见等使程序在时间推移的过程中一直在做扩展和增强,减少已有的抽象的修改(例如接口制定了,以后都不用再修改,这个肯定是我们最希望的);我们需要朝这个方向去努力,拥抱变化的同时,越少打破这个原则

28大设计模式

1. 单例模式

最简单常用的模式,不展开了

public Sun {
    private static final Sun sun = new Sun();
    public static Sun getInstance() {
        return sun;
    }
    public void light() {
        System.out.println("太阳发光");
    }
}

2. 工厂方法模式

Define an interface for creating an object,but let subclasses decide which class to
instantiate.Factory Method lets a class defer instantiation to subclasses. (定义一个用于创建对象的
接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类。)
优点:
1.  良好的封装性,代码结构清晰
2. 良好的扩展性(横向扩展,例如例子中需要新增一种食物)
3. 解耦,散落到各处的类似的初始化逻辑可以被封装,不用重复写,也提高维护性
应用:
1. 可简化成 简单工厂模式 用静态方法代替工厂类
2. 升级成多工厂
3. 单例模式的替代品
4. 延迟初始化
public interface Food {
    void name();
}

public class Bread implements Food {
    @Override
    public void name() {
        System.out.println("我是面包");
    }
}

public class Cookie implements Food {
    @Override
    public void name() {
        System.out.println("我是饼干");
    }
}

public class FoodFactory {
    public <T extends Food> T productFood(Class<T> cls) {
        T t = null;
        try {
            t = cls.newInstance();
        } catch (Exception e) {
            System.out.println("我不会生产这个");
            e.printStackTrace();
        }
        return t;
    }
    public static void main(String[] args) {
        FoodFactory factory = new FoodFactory();
        Bread bread = factory.productFood(Bread.class);
        bread.name();
        Cookie cookie = factory.productFood(Cookie.class);
        cookie.name();
    }
}

3. 抽象工厂模式

Provide an interface for creating families of related or dependent objects without specifying their concrete classes.(为创建一组相关或相互依赖的对象提供一个接口,而且无须指定它们 的具体类。)

优点:
1. 抽象封装
2. 分类约束
缺陷:
1. 子工厂扩展受限

public interface Food {
    void name();
}

public class SweetBread implements Food {
    @Override
    public void name() {
        System.out.println("我是甜面包");
    }
}

public class SaltyBread implements Food {
    @Override
    public void name() {
        System.out.println("我是咸面包");
    }
}

public class Cookie implements Food {
    @Override
    public void name() {
        System.out.println("我是饼干");
    }
}

public abstract class FoodFactory {
    <T extends Bread> T productFood(Class<T> cls);
}

public class BreadFactory extends FoodFactory {
    public <T extends Bread> T productFood(Class<T> cls) {
        T t = null;
        try {
            t = cls.newInstance();
        } catch (Exception e) {
            System.out.println("我不会生产这个");
            e.printStackTrace();
        }
        return t;
    }
    public static void main(String[] args) {
        FoodFactory factory = new BreadFactory();
        SweetBread bread1 = factory.productFood(SweetBread.class);
        bread1.name();
        SaltyBread bread2 = factory.productFood(SaltyBread.class);
        cookie2.name();
    }
}

4. 模板方法模式

对于业务模型的业务动作模板化;基本方法在子类中实现,模板方法在父类中调度

优点:
1. 封装固定部分,扩展可变部分
2. 公共代码提取,统一行为控制
缺陷:
1. 行为流程固化(之前是抽象的是一个动作;现在是抽象了动作和动作的顺序)
2. 复杂场景新手阅读不适(串联逻辑时,一下到子类,一下到父类,绕晕)
public abstract class Car {
    public abstract void start();
    public abstract void drive();
    public abstract void stop();
    public void run() {
        start();
        drive();
        stop();
    }
}
public class HongQiCar extends Car {
    @Override
    public void start() {
        System.out.println("红旗车启动");
    }
    @Override
    public void drive() {
        System.out.println("红旗车行驶");
    }
    @Override
    public void stop() {
        System.out.println("红旗车停止");
    }
    public static void main(String[] args) {
        Car car = new HongQiCar();
        car.run();
    }
}

5. 建造者模式

建造者模式(Builder Pattern)也叫做生成器模式

Separate the construction of a complex object from its representation so that the same
construction process can create different representations. (将一个复杂对象的构建与它的表示分
离,使得同样的构建过程可以创建不同的表示。)
优点:
良好的封装性和抽象
易于扩展
和工厂模式类似都是在创建类实例,但是不同的是,建造者开放了一部分流程给外部定制化
public abstract class CarModel {
    /**
     * 装备顺序
     */
    private ArrayList<String> sequence = new ArrayList<String>();
    /**
     * 发动
     */
    public abstract void engine();
    /**
     * 喇叭
     */
    public abstract void horn();
    /**
     * 挂挡
     */
    public abstract void gearbox();
    /**
     * 加油
     */
    public abstract void gas();
    /**
     * 停车
     */
    public abstract void stop();
    /**
     * 介绍
     */
    public void drive() {
        for (String step : sequence) {
            if ("engine".equals(step)) {
                engine();
            } else if ("horn".equals(step)) {
                horn();
            } else if ("gearbox".equals(step)) {
                gearbox();
            } else if ("gas".equals(step)) {
                gas();
            } else if ("stop".equals(step)) {
                stop();
            }
        }
    }
    /**
     * 设置装配顺序
     */
    public void setSequence(ArrayList<String> sequence) {
        this.sequence = sequence;
    }
}
public abstract class CarBuilder {
    // 顺序
    public abstract void setSequence(ArrayList<String> sequence);
    // 设置顺序后,获取模型
    public abstract CarModel getCarModel();
}
public class HongqiCarModel extends CarModel {
    @Override
    public void engine() {
        System.out.println("红旗车无钥匙启动汽车");
    }
    @Override
    public void horn() {
        System.out.println("红旗车按响喇叭");
    }
    @Override
    public void gearbox() {
        System.out.println("红旗车挂前进挡");
    }
    @Override
    public void gas() {
        System.out.println("红旗车加速");
    }
    @Override
    public void stop() {
        System.out.println("红旗车刹车停车");
    }
}
public class HoneqiCarBuilder extends CarBuilder {
    private HongqiCarModel carModel = new HongqiCarModel();
    @Override
    public void setSequence(ArrayList<String> sequence) {
        carModel.setSequence(sequence);
    }
    @Override
    public CarModel getCarModel() {
        return carModel;
    }

    public static void main(String[] args) {

    }
}
public class HongqiDirector {
    private HoneqiCarBuilder honeqiCarBuilder = new HoneqiCarBuilder();
    /**
     * A型号车需要先鸣笛才能开
     * @return
     */
    public HongqiCarModel getHongqiModelA() {
        ArrayList<String> sequence = new ArrayList<>();
        sequence.add("horn");
        sequence.add("engine");
        sequence.add("gearbox");
        sequence.add("gas");
        sequence.add("stop");
        HoneqiCarBuilder builder = new HoneqiCarBuilder();
        builder.setSequence(sequence);
        return (HongqiCarModel) builder.getCarModel();
    }
    /**
     * A型号车不需要先鸣笛能开
     * @return
     */
    public HongqiCarModel getHongqiModelB() {
        ArrayList<String> sequence = new ArrayList<>();
        sequence.add("engine");
        sequence.add("gearbox");
        sequence.add("gas");
        sequence.add("stop");
        HoneqiCarBuilder builder = new HoneqiCarBuilder();
        builder.setSequence(sequence);
        return (HongqiCarModel) builder.getCarModel();
    }
}

6. 代理模式

代理模式(Proxy Pattern)
Provide a surrogate or placeholder for another object to control access to it. (为其他对象提供
一种代理以控制对这个对象的访问。)
优点:
  • 高扩展性,不侵入实现层的源码
  • 职能清晰

缺点:

  • 代理在实际项目使用过程中,还是不要下放给所有人,实际经验中,AOP的随意使用,经常造成各种问题,特殊场景下的报错、代码可读性降低(大项目中,新人不知道哪里把数据修改了,代码不易理解,修改引入缺陷,测试不好覆盖场景)
public interface ITask {
    void doJob();
}
public class TaskImpl implements ITask {
    @Override
    public void doJob() {
        System.out.println("doing job");
    }
}
// 动态代理
public class DynamicProxy implements InvocationHandler {
    private Object object;
    public DynamicProxy(Object object) {
        this.object = object;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("before do job...");
        Object result = method.invoke(object, args);
        System.out.println("after do job...");
        return result;
    }
}
// 静态代理
public class StaticProxy implements ITask {
    private ITask task;
    public StaticProxy(final ITask task) {
        this.task = task;
    }

    @Override
    public void doJob() {
        System.out.println("before do job...");
        task.doJob();
        System.out.println("after do job...");
    }
}
public class TestProxy {
    public static void main(String[] args){
        ITask task = new TaskImpl();
        StaticProxy p1 = new StaticProxy(task);
        task.doJob();
        System.out.println("********************************");
        p1.doJob();
        System.out.println("********************************");
        ITask dynamic = (ITask)Proxy.newProxyInstance(ITask.class.getClassLoader(), task.getClass().getInterfaces(), new DynamicProxy(task));
        dynamic.doJob();
    }
}

Java中有JDK动态代理和cglib动态代理,可自行研究;动态代理是非常常见和使用的设计模式,对于业务前后统一的处理都可使用此模式;也叫AOP(Aspect Oriented Programming面向切面编程),还有一些名词:切面 Aspect,切入点 JoinPoint,通知 Advice,织入 Weave;概念一大堆,大家还是要从实际场景去考虑我们引入代理到底带来了什么,失去什么,是否合适当前的场景;从个人浅显的工作经验上来看,AOP下放给开发在业务中使用时,经常是弊大于利,谈虎色变

7. 原型模式

原型模式(Prototype Pattern
Specify the kinds of objects to create using a prototypical instance,and create new objects by
copying this prototype. (用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对
象。)
原型模式的核心是一个 clone 方法,通过该方法进行对象的拷贝, Java 提供了一个Cloneable 接口来标示这个对象是可拷贝的
优点:
  • 对于构造函数消耗的性能非常多且实例化的内容是一致的对象能够大幅提高性能
  • 双刃剑:逃避构造函数约束(成也萧何,败也萧何,千万注意此点对业务的影响)

缺点:

  • 仅深拷贝本对象(属性如果是对象则为浅拷贝)
  • clone和final一山不容二虎,成员变量加final后直接编译报错
public class Thing implements Cloneable {
    private ArrayList<String> arrayList = new ArrayList<String>();
    @Override
    public Thing clone() {
        Thing thing = null;
        try {
            thing = (Thing) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return thing;
    }
    public void setValue(String value) {
        this.arrayList.add(value);
    }
    public ArrayList<String> getValue() {
        return this.arrayList;
    }
}
public class Test {
    public static void main(String[] args) {
        Thing t = new Thing();
        // 仅深拷贝当前对象,内部的List为浅拷贝
        Thing clone = t.clone();
    }
}

8. 中介者模式

Define an object that encapsulates how a set of objects
interact.Mediator promotes loose coupling by keeping objects from referring to each other
explicitly,and it lets you vary their interaction independently. (用一个中介对象封装一系列的对象
交互,中介者使各对象不需要显示地相互作用,从而使其耦合松散,而且可以独立地改变它
们之间的交互。)
优点:
减少类间关系,从一对多编程一对一
缺点:
中介类肿胀
例如MVC的Controller层就属于中介者模式

9. 命令模式

命令模式是一个高内聚的模式
Encapsulate a request as an object,thereby
letting you parameterize clients with different requests,queue or log requests,and support undoable operations.(将一个请求封装成一个对象,从而让你使用不同的请求把客户端参数化,对请求排队或者记录请求日志,可以提供命令的撤销和恢复功能。)
优点:
类间解耦
可扩展性
缺点:
Command子类会非常多,这个类非肿胀
public abstract class Receiver {
    public abstract void doSomething();
}
public class ReceiverImpl1 extends Receiver {
    @Override
    public void doSomething() {
        System.out.println("On my building");
    }
}
public class Invoker {
    private Command command;
    public void setCommand(Command command) {
        this.command = command;
    }
    public void action() {
        this.command.execute();
    }
}
public abstract class Command {
    public abstract void execute();
}
public class CommandImpl1 extends Command {
    private Receiver receiver;
    public CommandImpl1(Receiver receiver) {
        this.receiver = receiver;
    }
    public void execute() {
        this.receiver.doSomething();
    }
}
public class Test {
    public static void main(String[] args) {
        Invoker invoker = new Invoker();
        // 定义接收者
        Receiver receiver = new ReceiverImpl1();
        // 定义一个发送给接收者的命令
        Command command = new CommandImpl1(receiver);
        // 把命令交给调用者去执行
        invoker.setCommand(command); invoker.action();
    }
}

10. 责任链模式

Avoid coupling the sender of a request to its receiver by giving more than one object a chance to
handle the request.Chain the receiving objects and pass the request along the chain until an object handles it.(使多个对象都有机会处理请求,从而避免了请求的发送者和接受者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有对象处理它为止。)
优点:
解耦,(请求和处理分开)
缺点:
“太解耦”,链路太长时,性能差且不好阅读和排查问题

11. 装饰模式

装饰模式( Decorator Pattern
优点:
  • 装饰类和被装饰类可以独立发展,而不会相互耦合。
  • 装饰模式是继承关系的一个替代方案
  • 动态扩展类的功能

缺点:

  • 不要套太多,套娃太深,复杂度太高,排查和理解耗时
public class Decoratee {
    public Decoratee() {
        whatIHave = new ArrayList<>();
        whatIHave.add("锦");
        whatIHave();
    }
    private List<String> whatIHave;
    public void gain(String str) {
        whatIHave.add(str);
    }
    public void whatIHave() {
        if(whatIHave.size() == 0) {
            System.out.println("我什么都没有!!!");
        } else {
            System.out.print("我有");
            for(String have : whatIHave) {
                System.out.print(have+" ");
            }
            System.out.println();
        }
    }
}
public class Decorator {
    private Decoratee decoratee;
    public Decorator(Decoratee decoratee) {
        this.decoratee = decoratee;
    }
    public void decorate() {
        decoratee.gain("花");
        decoratee.whatIHave();
    }
    public static void main(String[] args){
        Decorator decorator = new Decorator(new Decoratee());
        decorator.decorate();
    }
}

12. 策略模式

策略模式(Strategy Pattern
Define a family of algorithms,encapsulate each one,and make them interchangeable. (定义一组
算法,将每个算法都封装起来,并且使它们之间可以互换。)
优点:
  • 策略自由切换,方便横向扩展
  • 避免多重条件判断

缺点:

  • 策略类数量多时,需要有诸葛亮一样的人来运筹帷幄,复用性也低
  • 所有策略都得对外暴露
public interface AttackStrategy {
    /**
     * 五行法术攻击
     */
    void fiveElementsMagicAttack();
}
public class AttackStrategyGold implements AttackStrategy {
    @Override
    public void fiveElementsMagicAttack() {
        System.out.println("使用金系法术攻击");
    }
}
public class AttackStrategyWood implements AttackStrategy {
    @Override
    public void fiveElementsMagicAttack() {
        System.out.println("使用木系法术攻击");
    }
}
public class AttackStrategyWater implements AttackStrategy {
    @Override
    public void fiveElementsMagicAttack() {
        System.out.println("使用水系法术攻击");
    }
}
public class AttackStrategyFire implements AttackStrategy {
    @Override
    public void fiveElementsMagicAttack() {
        System.out.println("使用火系法术攻击");
    }
}
public class AttackStrategySoil implements AttackStrategy {
    @Override
    public void fiveElementsMagicAttack() {
        System.out.println("使用土系法术攻击");
    }
}
public class Context {
    AttackStrategy attackStrategy;
    public Context(AttackStrategy attackStrategy) {
        this.attackStrategy = attackStrategy;
        System.out.println("指挥部制定攻击策略");
    }
    public AttackStrategy getAttackStrategy() {
        return attackStrategy;
    }
}
public class Battle {
    public static String[] enemy = {"金系敌人", "木系敌人", "水系敌人", "火系敌人", "土系敌人"};
    public static void main(String[] args) {
        for (int i=0; i<5; i++) {
            System.out.println("---------------------------");
            attack();
        }
    }
    private static void attack() {
        int idx = (int)(Math.random() * 5);
        Context headquarters;
        System.out.println("我方遭遇:" + enemy[idx]);
        switch (enemy[idx]) {
            case "金系敌人":
                headquarters = new Context(new AttackStrategyFire());
                break;
            case "木系敌人":
                headquarters = new Context(new AttackStrategyGold());
                break;
            case "水系敌人":
                headquarters = new Context(new AttackStrategySoil());
                break;
            case "火系敌人":
                headquarters = new Context(new AttackStrategyWater());
                break;
            case "土系敌人":
                headquarters = new Context(new AttackStrategyWood());
                break;
            default:
                headquarters = new Context(new AttackStrategyGold());
        }
        headquarters.getAttackStrategy().fiveElementsMagicAttack();
    }
}

13. 适配器模式

适配器模式( Adapter Pattern
Convert the interface of a class into another interface clients expect.Adapter lets classes work
together that couldn't otherwise because of incompatible interfaces. (将一个类的接口变换成客户
端所期待的另一种接口,从而使原本因接口不匹配而无法在一起工作的两个类能够在一起工
作。)
适配器模式又叫做变压器模式,也叫做包装模式( Wrapper ),但是包装模式可不止一
优点:
  • 适配器模式可以让两个没有任何关系的类在一起运行,只要适配器这个角色能够搞定 他们就成
  • 降低了类的存在感,简化使用
  • 提供类的复用和灵活性

缺点:

  • 个人人为也不算缺点吧,项目初期不用考虑这个模式,大多在维护阶段去使用,去适配未知的场景(就像在中国是220的电,做的充电器就按220的做,以后去了未知的地区,再考虑做新的,因为电压,插口大小都是未知的)
  • 而且是不符合原有设计的时候才会发生,使用此改造也可能代码比较大的改造量
public interface DC5V {
    int outDC5v();
}

public class ChargeAdapter extends AC220V implements DC5V {
    @Override
    public int outDC5v() {
        int i = outAC220v();
        i = i / 44;
        System.out.println("转换成"+i+"V直流电!");
        return i;
    }
}

public class AC220V {
    int outAC220v() {
        int v = 220;
        System.out.println("输出220v交流电!!!");
        return v;
    }
}

public class Mobile {
    void charge(DC5V charger) {
        int i = charger.outDC5v();
        System.out.println("charging...");
    }
    public static void main(String[] args) {
        Mobile mobile = new Mobile();
        mobile.charge(new ChargeAdapter());
    }
}

14. 迭代器模式

迭代器模式(Iterator Pattern )目前已经是一个没落的模式,基本上没人会单独写一个迭
代器
Provide a way to access the elements of an aggregate object sequentially without exposing its
underlying representation. (它提供一种方法访问一个容器对象中各个元素,而又不需暴露该
对象的内部细节。)

public interface Iterator {
    //遍历到下一个元素 
    public Object next(); 
    //是否已经遍历到尾部 
    public boolean hasNext(); 
    //删除当前指向的元素 
    public boolean remove(); 
}

Java本身已经提供了很强大的基础API,所以我们基本不需要自己再去写这些

15. 组合模式

组合模式 (Composite Pattern) 也叫合成模式,有时又叫做部分 -整体模式(Part-Whole),主要是用来描述部分与整体的关系
Compose objects into tree structures to represent part-whole hierarchies.Composite lets clients treat individual objects and compositions of objects uniformly.(将对象组合成树形结构以表示 部分 - 整体 的层次结构,使得用户对单个对象和组合对象的使用具有一致性。)
优点:
1. 整体部分调用简单
2. 部分增加自由
缺点:
1. 部分没有定义,直接使用了实现类(对于面向接口编程的理念是不一致的,与依赖导致原则冲突)
使用场景
1. 维护部分和整体的关系,树形菜单、目录结构、组织架构
2. 从一个整体中能独立出去的部分

16. 观察者模式

观察者模式( Observer Pattern)也叫做发布订阅模式(Publish/subscribe ,它是一个在项 目中经常使用的模式
Define a one-to-many dependency between objects so that when one object changes state,all its dependents are notified and updated automatically.(定义对象间一种一对多的依赖关系,使得每 当一个对象改变状态,则所有依赖于它的对象都会得到通知并被自动更新。)
几个主要的组成部分
1. 被观察者
定义被观察者,和主要的动作
2. 观察者
定义观察者收到消息后的动作
3. 具体的被观察者
具体的被观察动作
4. 具体的观察者
具体的观察者动作
/**
 * 被观察者
 */
public interface Observable {
    // 增加一个观察者
    void addObserver(Observer observer);
    // 删除一个观察者
    void deleteObserver(Observer observer);
    // 既然要观察,我发生改变了他也应该有所动作,通知观察者
    void notifyObservers(String context);
}

/**
 * 观察者
 */
public interface Observer {
    void action(String info);
}

/**
 * 具体的被观察对象
 */
public class Student implements Observable {
    private String name;
    public Student(String name) {
        this.name = name;
    }
    private List<Observer> observers = new ArrayList<>();
    @Override
    public void addObserver(Observer observer) {
        observers.add(observer);
    }
    @Override
    public void deleteObserver(Observer observer) {
        observers.remove(observer);
    }
    @Override
    public void notifyObservers(String context) {
        for (Observer observer : observers) {
            observer.action(context);
        }
    }
    public void quWangBa() {
        System.out.println(name + "去网吧");
        notifyObservers(name + "去网吧");
    }
}

/**
 * 具体的建观察者
 */
public class Father implements Observer {
    @Override
    public void action(String info) {
        System.out.println("由于" + info + "被父亲知道,父亲教了他一套军体拳");
    }
}

/**
 * 具体的观察者
 */
public class Teacher implements Observer {
    @Override
    public void action(String info) {
        System.out.println("由于" + info + "被老师知道,老师要求其写检讨,并通报批评");
    }
}

public class Client {
    public static void main(String[] args) {
        Father father = new Father();
        Teacher teacher = new Teacher();
        Student student = new Student("张三");
        student.addObserver(teacher);
        student.addObserver(father);

        student.quWangBa();
    }
}

17. 门面模式

门面模式(Facade Pattern)也叫做外观模式,是一种比较常用的封装模式,其定义如
下:
Provide a unified interface to a set of interfaces in a subsystem.Facade defines a higher-level
interface that makes the subsystem easier to use.(要求一个子系统的外部与其内部的通信必须通
过一个统一的对象进行。门面模式提供一个高层次的接口,使得子系统更易于使用。)

个人理解门面模式是定义一个统一的规范,不论用什么方式实现,出入的“门面”是固定的,且不暴露门面内部的逻辑

public class Facade {
    private Worker worker = new Worker();

    public void doSomething() {
        // 对外暴露的方法固定,内部的实现目前是工人1去做,后续需要调整可以是其他的工人去做
        worker.doSomething();
    }
}

public class Worker {
    public void doSomething() {

    }
}

优点:

1. 减少系统的互相依赖

2. 提高门面内部的灵活性和自主性

3. 提高安全性(这个其实也是减少互相依赖带来的好处,减少了代码的侵入)

缺点:

不符合开闭原则,对修改关闭,对扩展开放,facade类定义好后,出现问题的话,唯一能做的一件事就是修改门面代码,这会影响所有人,所以很多时候,在门面对于一部分场景正确,但对一部分场景错误时,经常会开“后门”,再开一扇门,搞的次数多了,越来越复杂,越来越难以理解,越来越难维护。

门面如果能让优秀的人定义好的话,可以减少低水平开发人员对系统冲击,最差就是将其实现的代码重写,不会对整体造成影响;正如缺点中讲的,也是双刃剑,慎之又慎。

18. 备忘录模式

备忘录模式( Memento Pattern )提供了一种弥补真实世界缺陷的方法,让 后悔药 在程
序的世界中真实可行,其定义如下:
Without violating encapsulation,capture and externalize an object's internal state so that the
object can be restored to this state later. (在不破坏封装性的前提下,捕获一个对象的内部状
态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到原先保存的状态。)

备忘录就像我们处理数据库事务一样,可以对操作进行回滚

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.state = memento.getState();
    }

    public static void main(String[] args) {
        Caretaker caretaker = new Caretaker();

        Originator originator = new Originator();
        originator.setState("小明正在写PPT");

        System.out.println("老板让小明去接待客户");
        System.out.println("小明先记录备忘录");
        caretaker.setMemento(originator.createMemento());

        originator.setState("小明正在接待客户");
        System.out.println("小明接待完客户,继续去写PPT");

        originator.restoreMemento(caretaker.getMemento());
        System.out.println(originator.getState());
    }
}

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;
    }
}

public class Caretaker {
    private Memento memento;

    public Memento getMemento() {
        return memento;
    }

    public void setMemento(Memento memento) {
        this.memento = memento;
    }
}

应用场景

  • 保存恢复数据的场景
  • 数据旁路处理的场景
  • 实际应用过程大多会进行变形,所以其实没有很标准的落地形式,算一种抽象定义

注意点

  • 备忘录的生命周期管理,防止出现内存泄漏,业务脏数据等问题
  • 备忘录创建的性能问题,大量且频繁创建销毁,要考虑内存和CPU的消耗
  • 单纯的整对象备份,可以用Cloneable代替Memento类

大话西游中,周星驰在洞口月光下对着月光宝盒大喊“波若波若蜜”,来回穿梭时光回去救白晶晶,结果频繁操作,旁边的观察者吴孟达一脸懵逼,然后程序不稳定,穿越回了500年前...

18. 访问者模式

访问者模式( Visitor Pattern )是一个相对简单的模式,其定义如下: Represent an
operation to be performed on the elements of an object structure. Visitor lets you define a new
operation without changing the classes of the elements on which it operates. (封装一些作用于某种
数据结构中的各元素的操作,它可以在不改变数据结构的前提下定义作用于这些元素的新的
操作。)

public abstract class Element {
    // 定义业务逻辑
    public abstract void doSomething();

    // 允许谁来访问
    public abstract void accept(IVisitor visitor);
}

public class ConcreteElement1 extends Element {
    // 完善业务逻辑
    public void doSomething() {
        // 业务处理
    }

    // 允许那个访问者访问
    public void accept(IVisitor visitor) {
        visitor.visit(this);
    }
}

public class ConcreteElement2 extends Element {
    // 完善业务逻辑
    public void doSomething() {
        // 业务处理
    }

    // 允许那个访问者访问
    public void accept(IVisitor visitor) {
        visitor.visit(this);
    }
}

public interface IVisitor {
    // 可以访问哪些对象
    void visit(ConcreteElement1 el1);

    void visit(ConcreteElement2 el2);
}

public class Visitor implements IVisitor {
    // 访问el1元素
    public void visit(ConcreteElement1 el1) {
        el1.doSomething();
    }

    // 访问el2元素
    public void visit(ConcreteElement2 el2) {
        el2.doSomething();
    }
}

public class Client {
    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            // 获得元素对象
            Element el = createElement();
            // 接受访问者访问
            el.accept(new Visitor());
        }
    }

    public static Element createElement() {
        Random rand = new Random();
        if (rand.nextInt(100) > 50) {
            return new ConcreteElement1();
        } else {
            return new ConcreteElement2();
        }
    }
}

优点:

  • 符合单一职责原则
  • 可扩展性
  • 灵活性

缺点:

  • 违背依赖倒置原则(访问者依赖具体元素,而非抽象元素),很多时候我们在设计开发时会说,不是抽象的,那我抽象不就行了,但除了从代码上是抽象类或者接口,业务上真的是抽象的吗?我们口中的抽象真的是抽象吗?
  • 维护麻烦,对于元素增加、修改、删除都是麻烦事

19. 状态模式

Allow an object to alter its behavior when its internal state changes.The object will appear to change its class.(当一个对象内在状态改变时允许其改变行为,这个对象看起来像改变了其类。)

优点:

  • 结构清晰
  • 类符合单一职责和开闭原则,每个状态一个子类

缺点:

  • 有很多状态会扩展出很多子类

public abstract class LiftState {
    // 定义一个环境角色,也就是封装状态的变化引起的功能变化
    protected Context context;

    public void setContext(Context _context) {
        this.context = _context;
    }

    // 电梯开门
    public abstract void open();

    // 电梯关门
    public abstract void close();

    // 电梯运行
    public abstract void run();

    // 电梯停止
    public abstract void stop();
}

public class OpenningState extends LiftState {
    @Override
    public void open() {
        System.out.println("ACTION:电梯开门...");
    }

    @Override
    public void close() {
        // 状态修改
        super.context.setLiftState(Context.CLOSEING_STATE);
        // 动作委托为CloseState来执行
        super.context.getLiftState().close();
    }

    @Override
    public void run() {
        // 无此动作
        System.out.println("ERROR:开门状态不能运行");
    }

    @Override
    public void stop() {
        System.out.println("NO ACTION:原本就停着");
    }
}

public class ClosingState extends LiftState {
    @Override
    public void close() {
        System.out.println("ACTION:电梯门关闭...");
    }

    @Override
    public void open() {
        super.context.setLiftState(Context.OPENNING_STATE);
        // 置为敞门状态
        super.context.getLiftState().open();
    }

    @Override
    public void run() {
        super.context.setLiftState(Context.RUNNING_STATE);
        // 设置为运行状态
        super.context.getLiftState().run();
    }

    @Override
    public void stop() {
        super.context.setLiftState(Context.STOPPING_STATE);
        // 设置为停止状态
        super.context.getLiftState().stop();
    }
}

public class RunningState extends LiftState {
    @Override
    public void close() {
        // do nothing
        System.out.println("NO ACTION:运行时电梯门肯定是关闭的");
    }

    @Override
    public void open() {
        // do nothing
        System.out.println("ERROR:运行时不能开门");
    }

    @Override
    public void run() {
        System.out.println("ACTION:电梯运行...");
    }

    @Override
    public void stop() {
        super.context.setLiftState(Context.STOPPING_STATE);
        // 环境设置为停止状态
        super.context.getLiftState().stop();
    }
}

public class StoppingState extends LiftState {
    @Override
    public void close() {
        // do nothing;
        System.out.println("NO ACTION:停止时门是关着的");
    }

    @Override
    public void open() {
        super.context.setLiftState(Context.OPENNING_STATE);
        super.context.getLiftState().open();
    }

    @Override
    public void run() {
        super.context.setLiftState(Context.RUNNING_STATE);
        super.context.getLiftState().run();
    }

    @Override
    public void stop() {
        System.out.println("ACTION:电梯停止了...");
    }
}

public class Context {
    // 定义出所有的电梯状态
    public final static OpenningState OPENNING_STATE = new OpenningState();
    public final static ClosingState CLOSEING_STATE = new ClosingState();
    public final static RunningState RUNNING_STATE = new RunningState();
    public final static StoppingState STOPPING_STATE = new StoppingState();
    // 定义一个当前电梯状态,默认电梯是停止的
    private LiftState liftState;

    public LiftState getLiftState() {
        return liftState;
    }

    public void setLiftState(LiftState liftState) {
        this.liftState = liftState;
        // 把当前的环境通知到各个实现类中
        this.liftState.setContext(this);
    }

    public void open() {
        this.liftState.open();
    }

    public void close() {
        this.liftState.close();
    }

    public void run() {
        this.liftState.run();
    }

    public void stop() {
        this.liftState.stop();
    }

    public static void main(String[] args) {
        Context ctx = new Context();
        // 给一个初始状态
        ctx.setLiftState(STOPPING_STATE);
        ctx.open();
        ctx.close();
        ctx.run();
        ctx.stop();
    }
}

状态模式的实际应用场景其实很多,但是现实项目中终究一言难尽,设计模式是好的设计模式,但是最后在落地中,大家口中的shishan代码大部分由于业务状态引起,例如订单状态,各种券的状态等等,业务设计上没有完善的状态机,需求又经常调整场景破坏状态机,状态不由单一字段控制(当A表的a字段=1,B表的b字段=2时需要业务动作x),对于重大项目项目管理和测试又只允许增量修改,导致if写的到处都是,没人知道完整状态机,新人老人避之不及,吐槽程序shishan,这种情况都是大家一起造成的;希望大家在工作中能够遇到完善需求,完善设计,高效扩展,规范开发的项目,能离shishan远一些

未完待续...

-- 有缘登山,寒山不寒

  • 3
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值