大厂学院 设计模式

小结:开发时不要为了炫技而使用设计模式,适时参考即可

创建型


单例模式(singleton)


1.饿汉


例子:

public class Singleton {
    // 静态字段引用唯一实例:
    private static final Singleton INSTANCE = new Singleton();

    // 通过静态方法返回实例:
    public static Singleton getInstance() {
        return INSTANCE;
    }

    // private构造方法保证外部无法实例化:
    private Singleton() {
    }
}

总结:
    特点:上来就随着类的加载而创建实例
    缺点:非懒加载


 2.懒汉式及改进


例子:

package com.attoutouer.designPattern.singleton;

public class Lazy2 {

    private static volatile Lazy2 singleton;

    private Lazy2() {
        System.out.println("俺懒汉实例化了");
    }

    public  static Lazy2 getInstace(){
        if(singleton == null){
            synchronized (Lazy.class){
                if(singleton == null){
                    singleton = new Lazy2(); //error
                }
            }
        }
        return singleton;
    }
}


小结
        1.(双重检查锁的由来):
        为保证第一次创建对象时线程安全 在方法上加 synchronized,但会导致在第一次即使创建完对象了,不需要线程安全锁,线程每次也要等待,不能直接return拿到对象,效率太低。
        那为了提高效率 尝试锁范围缩小到判断里
        但又会使得第一次创建对象时 线程不安全,
        那就锁里面再加一次判断,解决
          2.用volatile修饰 是为了保证指令重排,理由如下
          上述1的写法看似解决了问题,但是仍有个很大的隐患:实例化对象的那行代码(标记为error的那行),实际上可以分解成以下三个步骤:

  1分配内存空间
  2初始化对象
  3将对象指向刚分配的内存空间

但是有些编译器为了性能的原因,可能会将第二步和第三步进行重排序,顺序就成了:

1分配内存空间
2将对象指向刚分配的内存空间
3初始化对象

现在考虑重排序后,两个线程发生了以下调用:

TimeThread AThread B
T1 检查到uniqueSingleton为空
T2 获取锁
T3 再次检查到uniqueSingleton为空
T4 为uniqueSingleton分配内存空间
T5 将uniqueSingleton指向内存空间
T6检查到uniqueSingleton不为空
T7访问uniqueSingleton(此时对象还未完成初始化)
T8初始化uniqueSingleton

在这种情况下,T7时刻线程B对uniqueSingleton的访问,访问的是一个初始化未完成的对象,所以要用volatile

3.枚举

例子:

public enum World { 
    // 唯一枚举: INSTANCE; 
    private String name = "world";
    public String getName() {
        return this.name; 
    } 
    public void setName(String name) { 
        this.name = name;
    } 
} 

总结: 有效防止反射攻击

工厂模式 (factory)

总结:

将if else 的判断生成交给子类完成

1.简单工厂: 一个具体工厂,if else

2.工厂方法和抽象工厂的区别: 后者有 多个抽象产品角色 每个具体工厂可以生成多类型产品

参考链接: 设计模式:工厂方法模式(Factory Method)和抽象工厂模式(Abstact Factory)-阿里云开发者社区

原型模式 (prototype)

例子:

public class PizzaStore1 implements Cloneable{

    ApplePizza applePizza ;
    public static Pizza orderPizza(String name){
        Pizza pizza = null;
        if("ApplePizza".equals(name)){
            pizza = new ApplePizza(name);
        }else if("BananaPizza".equals(name)){
            pizza = new BananaPizza(name);
        }

        return pizza;
    }

    public ApplePizza getApplePizza() {
        return applePizza;
    }

    public void setApplePizza(ApplePizza applePizza) {
        this.applePizza = applePizza;
    }

    @Override
    protected PizzaStore1 clone() throws CloneNotSupportedException {
        PizzaStore1 clone = (PizzaStore1) super.clone();
        clone.setApplePizza(clone.getApplePizza().clone());
        return clone;
    }
}

总结:
    实现Cloneable接口,实现深拷贝

建造者模式 (builder)

总结:
    当一个类的表示(属性)基本不变,但构建过程比较复杂时,可以将此过程抽象出来

参考链接:创建型-建造者模式-阿里云开发者社区

结构型

适配器模式 (adapter)

总结:
    将被适配的类按照某种规范进行改造。进阶:一般直接组合该类到adapter,当需要被改造的类比较多时,也可以在构造器处声明一个它们共属的接口。

例子:

public class AC220 {
    public int outputAC220V(){
        int output = 220;
        System.out.println("输出交流电"+output+"V");
        return output;
    }
}

public interface DC5 {
    int outputDC5V();
}

public class PowerAdapter implements DC5{
     AC220 ac220 = new AC220();
    @Override
    public int outputDC5V() {
        int i = ac220.outputAC220V();
        System.out.println("使用PowerAdapter输入AC"+i+"V"+"输出DC"+i/44+"V");
        return i/44;
    }
}

 桥接模式(bridge)

总结:
    1.将两种具有多个维度的类,进行组合,当然一般直接组合接口道另一个抽象类,不用第三者。避免类爆炸
    2.桥接模式实现比较复杂,实际应用也非常少,但它提供的设计思想值得借鉴,即不要过度使用继承,而是优先拆分某些部件,使用组合的方式来扩展功能。

例子:

public abstract class Iphone {
    Color color;

    public Iphone(Color color) {
        this.color = color;
    }

    public abstract void draw();

}

public interface Color {
    public void bePaint(String shape);
}

public class Blue implements Color {
    @Override
    public void bePaint(String shape) {
        System.out.println("蓝色的"+shape);
    }
}

public class XIAOMI extends Iphone{
    public XIAOMI(Color color) {
        super(color);
    }

    @Override
    public void draw() {
        color.bePaint("小米");
    }
}

public class Test {
    public static void main(String[] args) {
        Color blue = new Blue();
        Iphone xiaomi = new XIAOMI(blue);
        xiaomi.draw();
    }
}

 装饰器模式(decorator)

总结:
    1.
Decorator模式有什么好处?它实际上把核心功能和附加功能给分开了。核心功能指FileInputStream这些真正读数据的源头,附加功能指加缓冲、压缩、解密这些功能。如果我们要新增核心功能,就增加Component的子类,例如ByteInputStream。如果我们要增加附加功能,就增加Decorator的子类,例如CipherInputStream。两部分都可以独立地扩展,而具体如何附加功能,由调用方自由组合,从而极大地增强了灵活性
    2.实现上:

//创建原始的数据源:
InputStream fis = new FileInputStream("test.gz");
// 增加缓冲功能:
InputStream bis = new BufferedInputStream(fis);
// 增加解压缩功能:
InputStream gis = new GZIPInputStream(bis);

即 抽象的装饰器类的 构造器定义 核心组件的公属接口即可 

参考例子:装饰器 - 廖雪峰的官方网站

组合模式(composite)

总结:
    用于树形结构的类,特点: 有子节点的list,和对应的add,remove 方法。像文件夹和文件、GUI窗口的各种组件,都符合Composite模式的定义,因为它们的结构天生就是层级结构。

参考例子:
组合 - 廖雪峰的官方网站

外观模式(facade)

总结:
    将子系统的各个类整合起来,对外提供一个统一的入口,例如controller,网关。

例子:开公司:

// 工商注册:
public class AdminOfIndustry {
    public Company register(String name) {
        ...
    }
}

// 银行开户:
public class Bank {
    public String openAccount(String companyId) {
        ...
    }
}

// 纳税登记:
public class Taxation {
    public String applyTaxCode(String companyId) {
        ...
    }
}

public class Facade {
    public Company openCompany(String name) {
        Company c = this.admin.register(name);
        String bankAccount = this.bank.openAccount(c.getId());
        c.setBankAccount(bankAccount);
        String taxCode = this.taxation.applyTaxCode(c.getId());
        c.setTaxCode(taxCode);
        return c;
    }
}

享元模式(flyweight) 

总结:
    各种池化技术。连接池,线程池,Integer.valueof呀

参考例子:享元 - 廖雪峰的官方网站

代理模式(proxy)

总结:
    与adapter模式类似都是转化接口,不过Adapter模式,它用于把A接口转换为B接口,而Proxy模式不是把A接口转换成B接口,它还是转换成A接口。

常见三种代理:

静态代理:实现目标类的接口

public class UserDaoProxy implements IUserDao{

    private IUserDao target;
    public UserDaoProxy(IUserDao target) {
        this.target = target;
    }
    
    @Override
    public void save() {
        System.out.println("开启事务");//扩展了额外功能
        target.save();
        System.out.println("提交事务");
    }
}

jdk动态代理: 通过反射,需要目标类必须实现接口

cglib代理:用于没实现接口的目标类,它是一个第三方代码生成包。

参考例子:https://segmentfault.com/a/1190000011291179

行为型

模板方法模式(Template Method)

总结:
    抽象父类的方法 把主要骨架步骤完成,有变化的方法 作为抽像方法 交由子类实现。

参考例子:模板方法 - 廖雪峰的官方网站

命令模式(Command)

总结:
    一般的系统来讲,调用者与被调用者是紧耦合的,但当系统复杂到对命令本身进行管理(执行、撤销、记录等等),就可以考虑用命令模式。直观例子:遥控器就是人和家用电器之间的一个命令管理者。

参考例子:命令 - 廖雪峰的官方网站

访问者模式(Visitor)

总结:
    有一段数据结构和操作数据结构的方法混合代码,当我们希望增加类似操作方法时,为了避免修改源代码 就可以考虑此模式

例子:

public class FileStructure {
    private File rootDir;

    public FileStructure(File rootDir) {
        this.rootDir = rootDir;
    }

    public void handle(Visitor visitor){
        scan(rootDir,visitor);
    }

    private void scan(File rootDir, Visitor visitor) {
        for (File file : rootDir.listFiles()) {
            if (file.isFile()) {
                visitor.visitFile(file);
            } else if (file.isDirectory()) {
                visitor.visitFile(file);
                scan(file, visitor);
            }
        }
    }
}

public interface Visitor {
    public void visitFile(File file);
    public void visitDir(File dir);
}

public class JavaFileVisitor implements Visitor {
    @Override
    public void visitFile(File file) {
        if(file.getName().endsWith(".java")){
            System.out.println("Found java file: " + file);
        }
    }
    @Override
    public void visitDir(File dir) {
        System.out.println("Visit dir: " + dir.getName());
    }
}

public class MainTest {
    public static void main(String[] args) {
        FileStructure fileStructure = new FileStructure(new File("E:\\89sm\\code"));
        fileStructure.handle(new JavaFileVisitor());
    }
}

 迭代器模式(Iterator)

总结:
    让集合类都用统一的遍历方式,而不用关心其内部结构。记住:关键是返回一个Iterator对象,可以考虑内部类

例子:

public class ReverseArrayCollection<T> implements Iterable<T> {
    private T[] array;

    public ReverseArrayCollection(T... objs) {//改成这样是为了 不对外暴露集合类型
        this.array = Arrays.copyOfRange(objs,0,objs.length);
    }

    @Override
    public Iterator<T> iterator() {
        return new ReverseIterator();
    }
    class ReverseIterator implements Iterator<T>{
        int index;

        public ReverseIterator() {
            this.index = ReverseArrayCollection.this.array.length;
        }

        @Override
        public boolean hasNext() {//继续遍历的条件
            return index>0;
        }

        @Override
        public T next() {//将元素移动到下一位 并返回元素
            index--;
            return array[index];
        }
    }
}

public class MainTest {
    public static void main(String[] args) {
        ReverseArrayCollection<Integer> collection = new ReverseArrayCollection<>(1,2,3);
        for ( Iterator<Integer> iterator = collection.iterator();iterator.hasNext();){
            System.out.println("iterator.next() = " + iterator.next());
        }
    }
}

观察者模式(Observer)/ 发布订阅模式 

总结:
    抽象出一个Observer(消息接收方)接口,便于扩展消息接收方的类型。发布方在其内部维护

private List<ProductObserver> observers = new ArrayList<>();  

    // 注册观察者:
    public void addObserver(ProductObserver observer) {
        this.observers.add(observer);
    }

    // 取消注册:
    public void removeObserver(ProductObserver observer) {
        this.observers.remove(observer);
    }
即可。 实现 发布者和订阅者的解耦。

参考例子:观察者 - 廖雪峰的官方网站

中介者模式(Mediator)

总结:

        一.当发现多个类之间交互复杂 可以考虑中间者来引入它们 并模拟交互逻辑。
        二.这样每个类不需要知道它需要交互的类是哪些,也不需要写交互逻辑,都继承一个mediator,原来大篇幅交互逻辑 都只用一句话代替即可 this.mediator.sync(selfclassname,data); 例如 redis  mysql  es 之间的数据同步

参考例子:https://juejin.cn/post/6844903699643383821

备忘录模式(Momento)

总结:
    三个角色  
        发起者originator:对state进行操作,提供一个保存memento的方法   
        备忘录momento:只对state操作
        管理者caretaker:只对 momento进行操作

关键是 保存状态进备忘录的管理者时:发起者要提供一个有状态的备忘录对象给管理者 即:cr.setMemento(or.createMemento()); //保存状态

例子:

public class Originator {
    private String state;

    public String getState() {
        return state;
    }

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

    public Momento setMomento(){
        return new Momento(state);
    }
}

public class Momento {
    private String state;

    public String getState() {
        return state;
    }

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

    public Momento(String state) {
        this.state = state;
    }
}

public class Caretaker {
    private Momento momento;

    public Momento getMomento() {
        return momento;
    }

    public void setMomento(Momento momento) {
        this.momento = momento;
    }
}

public class Client {
    public static void main(String[] args) {
        Originator originator = new Originator();
        Caretaker caretaker = new Caretaker();
        originator.setState("1");
        System.out.println("初始状态为"+originator.getState());
        System.out.println("保存状态");
        caretaker.setMomento(originator.setMomento());
        originator.setState("2");
        System.out.println("设置状态为"+originator.getState());
        System.out.println("恢复状态为"+caretaker.getMomento().getState());
    }
}

解释器模式(Interpreter) 

 总结:
    定义语法树 对语句进行解析,如sql 和通配符 (了解即可); 

状态模式(State)

总结:
    当一个类的要干啥事是跟 状态切换有关的,就可以考虑用这个模式
    记住 两种核心角色: 一堆状态类 ,和管理这些实现切换状态的类

例子:

public class ThreadContext {
    private ThreadState state;

    public ThreadContext() {
       state = new New();
    }

    public ThreadState getState() {
        return state;
    }

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

    public void start() {
        ((New) state).start(this);
    }
    public void getCPU() {
        ((Runnable) state).getCPU(this);
    }
    public void suspend() {
        ((Running) state).suspend(this);
    }
    public void stop() {
        ((Running) state).stop(this);
    }
    public void resume() {
        ((Blocked) state).resume(this);
    }
}

public class New extends ThreadState {
    public New() {
        stateName = "新建状态";
        System.out.println("当前线程处于:新建状态.");
    }
    public void start(ThreadContext hj) {
        System.out.print("调用start()方法-->");
        if (stateName.equals("新建状态")) {
            hj.setState(new Runnable());
        } else {
            System.out.println("当前线程不是新建状态,不能调用start()方法.");
        }
    }
}
//具体状态类:就绪状态
class Runnable extends ThreadState {
    public Runnable() {
        stateName = "就绪状态";
        System.out.println("当前线程处于:就绪状态.");
    }
    public void getCPU(ThreadContext hj) {
        System.out.print("获得CPU时间-->");
        if (stateName.equals("就绪状态")) {
            hj.setState(new Running());
        } else {
            System.out.println("当前线程不是就绪状态,不能获取CPU.");
        }
    }
}
//具体状态类:运行状态
class Running extends ThreadState {
    public Running() {
        stateName = "运行状态";
        System.out.println("当前线程处于:运行状态.");
    }
    public void suspend(ThreadContext hj) {
        System.out.print("调用suspend()方法-->");
        if (stateName.equals("运行状态")) {
            hj.setState(new Blocked());
        } else {
            System.out.println("当前线程不是运行状态,不能调用suspend()方法.");
        }
    }
    public void stop(ThreadContext hj) {
        System.out.print("调用stop()方法-->");
        if (stateName.equals("运行状态")) {
            hj.setState(new Dead());
        } else {
            System.out.println("当前线程不是运行状态,不能调用stop()方法.");
        }
    }
}
//具体状态类:阻塞状态
class Blocked extends ThreadState {
    public Blocked() {
        stateName = "阻塞状态";
        System.out.println("当前线程处于:阻塞状态.");
    }
    public void resume(ThreadContext hj) {
        System.out.print("调用resume()方法-->");
        if (stateName.equals("阻塞状态")) {
            hj.setState(new Runnable());
        } else {
            System.out.println("当前线程不是阻塞状态,不能调用resume()方法.");
        }
    }
}
//具体状态类:死亡状态
class Dead extends ThreadState {
    public Dead() {
        stateName = "死亡状态";
        System.out.println("当前线程处于:死亡状态.");
    }
}

public class Client {
    public static void main(String[] args) {
        ThreadContext context = new ThreadContext();
        context.start();
        context.getCPU();
        context.suspend();
        context.resume();
        context.getCPU();
        context.stop();
    }
}

 策略模式(Strategy)

总结:
    在一段长代码中 把容易变化的 算法 抽离出来,作为参数传入 就可以考虑此模式
例如 Arrays.sort(T[] a, Comparator<? super T> c)这个排序方法 需要各种比较器作为策略传入 
参考例子:策略 - 廖雪峰的官方网站

责任链模式 (Chain of Responsibility)

总结:
    一、    在一段使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。
例如,JavaEE的Servlet规范定义的Filter就是一种责任链模式,它不但允许每个Filter都有机会处理请求,还允许每个Filter决定是否将请求“放行”给下一个Filter
    二、核心角色就是 HandlerChain  实现了链条的处理

public class HandlerChain {
    private List<Handler> handlers = new ArrayList<Handler>();
    public void addHandler(Handler handler) {
        this.handlers.add(handler);
    }

    public boolean handle(Request request){
        for (Handler handler : handlers) {
            Boolean r = handler.handle(request);
            if(r!=null){
                System.out.println(request + " " + (r ? "Approved by " : "Denied by ") + handler.getClass().getSimpleName());
                return r;
            }
        }
        throw new RuntimeException("Could not handle request: " + request);
    }
}

参考例子:责任链 - 廖雪峰的官方网站

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值