深入浅出设计模式

2024-07-26 19:56:03 浏览数 (1)

前言

设计模式目的是为了代码可重用,降低代码的耦合度,提高代码的可扩展性、可维护性,不可为了使用设计模式而过度设计,要平衡复杂度和灵活性。

设计原则

设计模式一般需要符合以下几个原则:

开闭原则

开放扩展,关闭修改。简单来说,在添加新功能时能不修改就不修改,通过新增代码可以实现那是最好的。

里氏替换原则

一种面向对象的设计原则。简单来说,一个方法如果可以通过父类调用成功,那么替换成子类调用也可以成功。

单一职责

一个类只负责一个功能领域中相应的职责。

迪米特法则

最少知道原则,不希望类之间建立直接联系,降低耦合度,如果真的需要希望通过中介通信。

目前常见的设计模式分为以下3个类型:

创建型模式

封装了系统中对象如何创建、组合等信息。重点关注如何创建对象,将对象的创建和使用分离。

工厂方法

通过工厂生产的形式获取对象,客户端引用抽象工厂及抽象产品,不关注如何创建。

应用案例:Spring的BeanFactory

代码示例:

代码语言:java复制
//工厂类
public interface IFactory {
    IProduct create();
}
代码语言:java复制
//具体工厂实现
public class FactoryA implements IFactory{
    @Override
    public IProduct create() {
        return new ProductA();
    }
}
代码语言:java复制
// 产品抽象类
public interface IProduct {
    void get();
}
代码语言:java复制
//具体产品实现
public class ProductA implements IProduct{
    @Override
    public void get() {}
}
代码语言:java复制
public class FactoryMain {
    public static void main(String[] args) {
        IFactory factory=new FactoryA();
        IProduct product=factory.create();
        product.get();
    }
}

抽象工厂

基于工厂方法,在工厂中生产多个产品,也是与其的区别。

代码示例:

代码语言:java复制
//抽象工厂类
public interface IFactory {
//抽象工厂中多个产品或产品组
    IProductA createA();
    IProductB createB();
}
代码语言:java复制
//抽象工厂具体实现
public class FactoryA implements IFactory {
//抽象工厂具体产品指向
    @Override
    public IProductA createA() {
        return new ProductA();
    }

    @Override
    public IProductB createB() {
        return new ProductB();
    }
}
代码语言:java复制
//产品或产品组A
public interface IProductA {
    void get();
}
代码语言:java复制
//产品或产品组B
public interface IProductB {
    void get();
}
代码语言:java复制
//产品或产品组A具体实现
public class ProductA implements IProductA{
    @Override
    public void get() {}
}
代码语言:java复制
//产品或产品组B具体实现
public class ProductB implements IProductB{
    @Override
    public void get() {}
}
代码语言:java复制
public class FactoryMain {
    public static void main(String[] args) {
        // FactoryA 工厂下的 IProductA和IProductB
        IFactory factoryA=new FactoryA();
        IProductA AproductA=factoryA.createA();
        AproductA.get();
        IProductB AproductB=factoryA.createB();
        AproductB.get();
    }
}

单例

保证一个类只有一个实例,并提供全局访问。

应用案例:Spring的SingletonBeanRegistry

代码示例:

代码语言:java复制
public class SingletenInstance {
    public static final SingletenInstance instance=new SingletenInstance();
    private SingletenInstance(){};
    public static void main(String[] args) {
        SingletenInstance instance1=SingletenInstance.instance;
        SingletenInstance instance2=SingletenInstance.instance;
        System.out.println(instance1==instance2);//true
    }
}

原型

创建对象时根据现有的对象创建。

应用案例:java的Object提供的clone()方法

代码示例:

代码语言:java复制
public class PrototypeObj implements Cloneable{
    @Override
    protected PrototypeObj clone() throws CloneNotSupportedException {
        return new PrototypeObj();
    }
    public static void main(String[] args) throws CloneNotSupportedException {
        PrototypeObj obj1=new PrototypeObj();
        PrototypeObj obj2=obj1.clone();
    }
}  

建造者(生成器)

将复杂对象的构建过程抽象出来,通过不同的实现构造不同表现的对象。

应用场景:StringBuilder

代码示例:

代码语言:java复制
public class BuilderObj {
    private String attr1;
    private String attr2;
    private String attr3;


    public BuilderObj build() {
        return this;
    }

    public BuilderObj setAttr1(String attr1) {
        this.attr1 = attr1;
        return this;
    }

    public BuilderObj setAttr2(String attr2) {
        this.attr2 = attr2;
        return this;
    }

    public BuilderObj setAttr3(String attr3) {
        this.attr3 = attr3;
        return this;
    }
    public static void main(String[] args) {
        BuilderObj obj = new BuilderObj().setAttr1("Attr1").setAttr3("Attr3").build();
    }
}

结构型模式

如何组合己有的类和对象以获得更大的结构。重点关注如何组合对象,通过组合对象灵活使用获得更好、更灵活的结构。

适配器

将一个接口转换成提供者需要的接口,通过对接口的转换使得两个本来不兼容的接口可以一起工作。

应用案例:ExecutorService对Callable及Runnable的兼容

代码示例:

代码语言:java复制
public class RunnableAdapter implements Runnable{
    private Callable callable;
	//适配Callable接口
    public RunnableAdapter(Callable callable) {
        this.callable=callable;
    }

    @Override
    public void run() {
        try {
            System.out.println(callable.call());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    public static void main(String[] args) {
        Callable callable =()->"I'm call";
        new Thread(new RunnableAdapter(callable)).start();
    }
}

装饰器

简单来说,动态给对象添加一些额外的功能。

应用案例:FilterInputStream就属于一个装饰类,其继承InputStream,所有InputStream可以通过FilterInputStream下面的类进行装饰获取额外功能,比如通过BufferedInputStream缓冲功能提高读取效率、通过DataInputStream读数据时可以直接转换java基本类型等。

代码示例:

代码语言:java复制
public interface Keyboard {
    public void print();
}
代码语言:java复制
public class KeyboardNormal implements Keyboard{
    public void print(){
        System.out.println("按键");
    }
}
代码语言:java复制
public class KeyboardLight implements Keyboard{
    private Keyboard keyboard;
	//装饰对象
    public KeyboardLight(Keyboard keyboard) {
        this.keyboard=keyboard;
    }

    public void print(){
        this.keyboard.print();
        System.out.println("闪光效果");
    }
}
代码语言:java复制
public class KeyboardVoice implements Keyboard{

    private Keyboard keyboard;
	//装饰对象
    public KeyboardVoice(Keyboard keyboard) {
        this.keyboard=keyboard;
    }

    public void print(){
        this.keyboard.print();
        System.out.println("声音效果");
    }
}
代码语言:java复制
    public static void main(String[] args) {
        Keyboard keyboardNormal=new KeyboardNormal();
        //装饰对象灯光效果
        Keyboard keyboardLight=new KeyboardLight(keyboardNormal);
        //装饰对象声音效果
        Keyboard keyboardVoice=new KeyboardVoice(keyboardLight);
        keyboardVoice.print();
        //按键
        //闪光效果
        //声音效果
    }

享元

核心思想是共享,如果一个对象一经创建很少变动,那直接返回共享实例即可(重复利用对象)。

应用案例:可以看下Integer.valueOf(),在初始化数据时如果是-128~127就直接返回数据。线程池等。

代码示例:

代码语言:java复制
public class FlyWeightObj {
    String cache = "value";
    public String get() {
        if (null != cache) {
            return cache;
        }
        return new String("chche");
    }
}

代理

通过一个代理对象控制一个对象的访问,和适配器模式相似,不过是同一接口。

应用案例:Spring AOP

代码示例:

代码语言:java复制
public class RunnableProxy implements Runnable{

    private Runnable runnable;
	//代理Runnable对象
    public RunnableProxy(Runnable runnable) {
        this.runnable=runnable;
    }

    @Override
    public void run() {
    	//Runnable执行代理
        System.out.println("do something");
        runnable.run();
        System.out.println("do something");
    }
    public static void main(String[] args) {
        new Thread(new RunnableProxy(()-> System.out.println("hi"))).start();
    }
}

外观

比较简单的一个模式,基本思想是:如果客户端需要和多个子系统交互,那么可以提供一个中介,客户端和中介交互,中介和各子系统交互。

应用案例:网关、nginx

组合

常用于树形数据结构,把叶子结点和父节点统一管理,比如菜单。

代码示例:

代码语言:java复制
public class Menu {

    private String name;
    
    private List<Menu> childMenu;
}

行为型模式

用于对象之间的职责及其提供服务的分配方式。重点关注对象如何协作,描述一组对象如何协作完成一个整体任务。

责任链

一种处理请求的模式,把多个处理器串成链,依次处理,能让多个处理器都有机会处理,或者某个处理器请求成功为止。

应用场景:Filter,Interceptor

代码示例:

代码语言:java复制
public interface Filter {
    boolean doFilter();
}
代码语言:java复制
public class Filter1 implements Filter{
    @Override
    public boolean doFilter() {
        System.out.println("Filter1.....");
        return false;
    }
}
代码语言:java复制
public class Filter2 implements Filter{
    @Override
    public boolean doFilter() {
        System.out.println("Filter2.....");
        return false;
    }
}
代码语言:java复制
//责任链
public class FilterChain {
    private List<Filter> filters=new ArrayList<>();
	//将多个处理器串联起来
    public void add(Filter filter){
        filters.add(filter);
    }
    public boolean doFilter(){
    	//串联执行
        for (Filter filter:filters){
            if (!filter.doFilter())return false;
        }
        return true;
    }

    public static void main(String[] args) {
        FilterChain chain=new FilterChain();
        chain.add(new Filter1());
        chain.add(new Filter2());
        chain.doFilter();
    }
}

备忘录

捕捉一个对象当时的状态,并在该对象之外保存这个状态,在某个时候可以恢复状态。

应用场景:几乎所有的软件都用到了备忘录模式。比如说保存、回退。

代码示例:

代码语言:java复制
public class Memento implements Serializable{
	//备忘对象
    private MementoObj obj = new MementoObj();

    private class MementoObj implements Serializable {
        private String state = "old";

        public void setState(String state) {
            this.state = state;
        }

        public String getState() {
            return state;
        }
    }
	//记录状态
    public void save() throws IOException {
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(new File("obj.data")));
        oos.writeObject(obj);
        oos.close();
    }
	//回滚状态
    public void load() throws IOException, ClassNotFoundException {
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(new File("obj.data")));
        obj= (MementoObj) ois.readObject();
        ois.close();
    }

    public static void main(String[] args) throws IOException, ClassNotFoundException {
        Memento memento=new Memento();
        memento.save();
        memento.obj.setState("new");
        System.out.println("新的状态" memento.obj.getState());
        memento.load();
        System.out.println("回归到旧的状态" memento.obj.getState());
    }
}

迭代器

提供统一的遍历接口,保证调用者以相同的接口遍历各种数据结构的集合。

应用场景:各个集合对Iterator具体实现,比如ArrayList、HashMap等。

观察者

简单理解:当一个对象状态发生改变时,所有观察它的对象都被通知。

应用场景:发布-订阅

代码示例:

代码语言:java复制
//观察者接口
public interface Observer {
    void publish(Event event);
}
//观察事件
enum Event {
    cry, hug
}
//具体观察者
class Mom implements Observer {
    @Override
    public void publish(Event event) {
        System.out.println(event   ",mom coming");
    }
}
//具体观察者
class Dad implements Observer {
    @Override
    public void publish(Event event) {
        System.out.println(event   ",wait wait");
    }
}
//被观察者
class Child {
    List<Observer> observerList = new ArrayList<>();
	//模拟注入观察者
    {
        observerList.add(new Mom());
        observerList.add(new Dad());
    }
	//被观察者某个事件发生后通知观察者
    public void action(Event event) {
        for (Observer observer : observerList) {
            observer.publish(event);
        }
    }
}

class ObserverMain {
    public static void main(String[] args) {
        Child child=new Child();
        child.action(Event.cry);
    }
}

模版方法

其核心思想是:定义一个操作的的一系列步骤,对于暂时不确定的步骤,交给子类实现就好。

代码语言:java复制
public abstract class TrafficTemplate {
	//定义骨架
    final void goHome() {
        int money = money();
        int time = time();
        System.out.println("花费"   money   "元,"   time   "小时");
    }

    protected abstract int time();

    protected abstract int money();

    public static void main(String[] args) {
        TrafficTemplate trafficTemplate = new Bicycle();
        trafficTemplate.goHome();
    }
}
代码语言:java复制
public class Bicycle extends TrafficTemplate{
    @Override
    protected int time() {
        return 2;
    }

    @Override
    protected int money() {
        return 0;
    }
}
代码语言:java复制
public class Car extends TrafficTemplate{
    @Override
    protected int time() {
        return 1;
    }

    @Override
    protected int money() {
        return 5;
    }
}

策略

定义一组算法并封装起来,运行时可以灵活的使用其中一个。其核心思想是讲容易变换的算法抽离出来作为“策略”参数传递。

应用场景:List.sort(Comparator) 其中Comparator可以看作策略。

代码示例:

代码语言:java复制
//策略接口
public interface Strategy {
    void toDO(AtomicInteger number);
}
代码语言:java复制
//具体策略实现
public class PlusStrategy implements Strategy{
    @Override
    public void toDO(AtomicInteger number) {
        number.incrementAndGet();
    }
}
代码语言:java复制
//具体策略实现
public class SubtractStrategy implements Strategy{
    @Override
    public void toDO(AtomicInteger number) {
        number.decrementAndGet();
    }
}
代码语言:java复制
public class DoSomething {
    @Override
    public void toDO(AtomicInteger number,Strategy plusStrategy) {
         plusStrategy.toDO(number);
    }
}
代码语言:java复制
    private static AtomicInteger number = new AtomicInteger();
    public static void main(String[] args) {
        DoSomething doSomething=new DoSomething();
        //传入策略执行
        doSomething.toDO(number,new SubtractStrategy());
        System.out.println(number.get());
    }

0 人点赞