深入浅出23种设计模式(最全面)

前言

有一些重要的设计原则在开篇和大家分享下,这些原则将贯通全文:

  • 面向接口编程,而不是面向实现。这个很重要,也是优雅的、可扩展的代码的第一步,这就不需要多说了吧。

  • 职责单一原则。每个类都应该只有一个单一的功能,并且该功能应该由这个类完全封装起来。

  • 对修改关闭,对扩展开放。对修改关闭是说,我们辛辛苦苦加班写出来的代码,该实现的功能和该修复的 bug 都完成了,别人可不能说改就改;对扩展开放就比较好理解了,也就是说在我们写好的代码基础上,很容易实现扩展。

创建型模式比较简单,但是会比较没有意思,结构型和行为型比较有意思

每个代理模式的代码都必须自己手动完成一遍。

创建型模式

创建型模式的作用就是创建对象,说到创建一个对象,最熟悉的就是 new 一个对象,然后 set 相关属性。但是,在很多场景下,我们需要给客户端提供更加友好的创建对象的方式,尤其是那种我们定义了类,但是需要提供给其他开发者用的时候。

工厂模式分为简单工厂模式,工厂模式,抽象工厂模式

在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。本质就是使用工厂方法代替new操作。

简单工厂模式

public class FoodFactory {
    public static Food makeFood(String name) {
        if (name.equals("兰州拉面")) {
            Food noodle = new LanZhouNoodle();
            System.out.println("兰州拉面"+noodle+"出锅啦");
            return noodle;
        } else if (name.equals("黄焖鸡")) {
            Food chicken = new HuangMenChicken();
            System.out.println("黄焖鸡"+ chicken +"出锅啦");
            return chicken;
        } else {
            System.out.println("不知道你做的什么哦~");
            return null;
        }
    }
}

其中,LanZhouNoodle 和 HuangMenChicken 都继承自 Food。

public class Cook {
    public static void main(String[] args) {
        Food food = FoodFactory.makeFood("黄焖鸡");
        FoodFactory.makeFood("jaja");
    }
}

简单地说,简单工厂模式通常就是这样,一个工厂类 XxxFactory,里面有一个静态方法,根据我们不同的参数,返回不同的派生自同一个父类(或实现同一接口)的实例对象。

我们强调职责单一原则,一个类只提供一种功能,FoodFactory 的功能就是只要负责生产各种 Food。

在此例中可以看出,Cook 类在使用 FoodFactory 时就不需要 new 任何一个对象,这就是简单工厂模式的好处,封装了 new 的部分,做到的代码易用性。

工厂模式

简单工厂模式很简单,如果它能满足我们的需要,我觉得就不要折腾了。之所以需要引入工厂模式,是因为我们往往需要使用两个或两个以上的工厂。

public interface FoodFactory {
    Food makeFood(String name);
}
public class ChineseFoodFactory implements FoodFactory {

    @Override
    public Food makeFood(String name) {
        if (name.equals("A")) {
            return new ChineseFoodA();
        } else if (name.equals("B")) {
            return new ChineseFoodB();
        } else {
            return null;
        }
    }
}
public class AmericanFoodFactory implements FoodFactory {

    @Override
    public Food makeFood(String name) {
        if (name.equals("A")) {
            return new AmericanFoodA();
        } else if (name.equals("B")) {
            return new AmericanFoodB();
        } else {
            return null;
        }
    }
}

其中,ChineseFoodA、ChineseFoodB、AmericanFoodA、AmericanFoodB 都派生自 Food。

客户端调用:

public class APP {
    public static void main(String[] args) {
        // 先选择一个具体的工厂
        FoodFactory factory = new ChineseFoodFactory();
        // 由第一步的工厂产生具体的对象,不同的工厂造出不一样的对象
        Food food = factory.makeFood("A");
    }
}

虽然都是调用 makeFood("A") 制作 A 类食物,但是,不同的工厂生产出来的完全不一样。

第一步,我们需要选取合适的工厂,然后第二步基本上和简单工厂一样。

核心在于,我们需要在第一步选好我们需要的工厂。比如,我们有 LogFactory 接口,实现类有 FileLogFactory 和 KafkaLogFactory,分别对应将日志写入文件和写入 Kafka 中,显然,我们客户端第一步就需要决定到底要实例化 FileLogFactory 还是 KafkaLogFactory,这将决定之后的所有的操作。

抽象工厂模式

提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。工厂模式是我们最常用的模式了,著名的Jive论坛 ,就大量使用了工厂模式,工厂模式在Java程序系统可以说是随处可见。

为什么工厂模式是如此常用?因为工厂模式就相当于创建实例对象的new,我们经常要根据类Class生成实例对象,如A a=new A() 工厂模式也是用来创建实例对象的,所以以后new时就要多个心眼,是否可以考虑实用工厂模式,虽然这样做,可能多做一些工作,但会给你系统带来更大的可扩展性和尽量少的修改量

  • 一个系统要独立于它的产品的创建、组合和表示时。
  • 一个系统要由多个产品系列中的一个来配置时。
  • 当你要强调一系列相关的产品对象的设计以便进行联合使用时。
  • 当你提供一个产品类库,而只想显示它们的接口而不是实现时。

AbstractFactory 声明一个创建抽象产品对象的操作接口。

ConcreteFactory 实现创建具体产品对象的操作。

AbstractProduct 为一类产品对象声明一个接口。

ConcreteProduct 定义一个将被相应的具体工厂创建的产品对象。 实现AbstractProduct接口。

Client 仅使用由AbstractFactoryAbstractProduct类声明的接口

示例: 

AbstractFactory:定义抽象工程类IAnimalFactory

package  com.pizilei.design.abstractfactory;
/**
*  这个接口就是类图中标识的
*  AbstractFactory抽象工厂
*  @author  pizilei
*
*/
public  interface  IAnimalFactory  {
    /**
      *  定义创建Icat接口实例的方法
      *  @return
      */
    ICat  createCat();
    /**
      *  定义创建IDog接口实例的方法
      *  @return
      */
    IDog  createDog();
}

ConcreteFactory创建抽象工厂类的两个实现类,WhiteAnimalFactoryBlackAnimalFactory

package  com.pizilei.design.abstractfactory;
/**
*  IAnimalFactory抽象工厂的实现类
*  @author  pizilei
*
*/
public  class  WhiteAnimalFactory  implements  IAnimalFactory  {
    @Override
    public  ICat  createCat()  {
          return  new  WhiteCat();
    }
    @Override
    public  IDog  createDog()  {
          return  new  WhiteDog();
    }

}


package  com.pizilei.design.abstractfactory;
/**
*  IAnimalFactory抽象工厂的实现类
*  @author  pizilei
*/
public  class  BlackAnimalFactory  implements  IAnimalFactory  {
    @Override
    public  ICat  createCat()  {
          return  new  BlackCat();
    }
    @Override
    public  IDog  createDog()  {
          return  new  BlackDog();
    }

}

AbstractProduct定义抽象工厂中要生产的抽象产品接口ICatIDog

package  com.pizilei.design.abstractfactory;
/**
*  类图中定义的AbstractProduct
*  指定工厂生产的产品
*  @author  pizilei
*
*/
public  interface  ICat  {
    /**
      *  定义方法
      */
    void  eat();
}


package  com.pizilei.design.abstractfactory;
/**
*  类图中定义的AbstractProduct
*  指定工厂生产的产品
*  @author  pizilei
*
*/
public  interface  IDog  {

    /**
      *  定义方法
      */
    void  eat();
}

ConcreteProduct创建产品的实现类BlackCatBlackDogWhiteCatWhiteDog

package  com.pizilei.design.abstractfactory;
/**
*  ICat接口的实现类
*  @author  pizilei
*
*/
public  class  BlackCat  implements  ICat  {
@Override
    public  void  eat()  {
          System.out.println("The  black  cat  is  eating!");
    }
}


package  com.pizilei.design.abstractfactory;

/**
*  IDog的实现类
*  @author  pizilei
*/
public  class  BlackDog  implements  IDog  {
@Override
    public  void  eat()  {
          System.out.println("The  black  dog  is  eating");
    }
}


package  com.pizilei.design.abstractfactory;
/**
*  ICat的实现类
*  @author  pizilei
*
*/
public  class  WhiteCat  implements  ICat  {
@Override
    public  void  eat()  {
          System.out.println("The  white  cat  is  eating!");
    }
}



package  com.pizilei.design.abstractfactory;
/**
*  IDog的实现类
*  @author  binghe
*
*/
public  class  WhiteDog  implements  IDog  {
@Override
    public  void  eat()  {
          System.out.println("The  white  dog  is  eating!");
    }

}

Client:定义一个测试类Test


/**
*  测试类
*  @author  pizilei
*
*/
public  class  Test  {
    public  static  void  main(String[]  args)  {
    IAnimalFactory  blackAnimalFactory  =  new  BlackAnimalFactory();
    ICat  blackCat  =  blackAnimalFactory.createCat();
    blackCat.eat();
    IDog  blackDog  =  blackAnimalFactory.createDog();
    blackDog.eat();
    
    IAnimalFactory  whiteAnimalFactory  =  new  WhiteAnimalFactory();
    ICat  whiteCat  =  whiteAnimalFactory.createCat();
    whiteCat.eat();
    IDog  whiteDog  =  whiteAnimalFactory.createDog();
    whiteDog.eat();
    }
}

输出结果:

The  black  cat  is  eating!
The  black  dog  is  eating
The  white  cat  is  eating!
The  white  dog  is  eating!

由此可见,工厂方法确实为系统结构提供了非常灵活强大的动态扩展机制,只要我们更换一下具体的工厂方法,系统其他地方无需一点变换,就有可能将系统功能进行改头换面的变化

单例模式

简单点说,就是一个应用程序中,某个类的实例对象只有一个,你没有办法去new,因为构造器是被private修饰的,一般通过getInstance()的方法来获取它们的实例。

getInstance()的返回值是一个对象的引用,并不是一个新的实例,所以不要错误的理解成多个对象。

特点

  • 类构造器私有
  • 持有自己类型的属性
  • 对外提供获取实例的静态方法

饿汉式写法

public class Singleton {  
   private static Singleton instance = new Singleton();  
   private Singleton (){}  
   public static Singleton getInstance() {  
   return instance;  
   }  
}

弊端:因为类加载的时候就会创建对象,所以有的时候还不需要使用对象,就会创建对象,造成内存的浪费;

饱汉模式最容易出错:

public class Singleton {
    // 首先,也是先堵死 new Singleton() 这条路
    private Singleton() {}
    // 和饿汉模式相比,这边不需要先实例化出来,注意这里的 volatile,它是必须的
    private static volatile Singleton instance = null;

    public static Singleton getInstance() {
        if (instance == null) {
            // 加锁
            synchronized (Singleton.class) {
                // 这一次判断也是必须的,不然会有并发问题
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

双重检查,指的是两次检查 instance 是否为 null。

volatile 在这里是需要的,希望能引起读者的关注。

很多人不知道怎么写,直接就在 getInstance() 方法签名上加上 synchronized,这就不多说了,性能太差。

嵌套类最经典,以后大家就用它吧:

public class Singleton {

    private Singleton() {}
    // 主要是使用了 嵌套类可以访问外部类的静态属性和静态方法 的特性
    private static class Holder {
        private static Singleton instance = new Singleton();
    }
    public static Singleton getInstance() {
        return Holder.instance;
    }
}

注意,很多人都会把这个嵌套类说成是静态内部类,严格地说,内部类和嵌套类是不一样的,它们能访问的外部类权限也是不一样的。

最后,我们说一下枚举,枚举很特殊,它在类加载的时候会初始化里面的所有的实例,而且 JVM 保证了它们不会再被实例化,所以它天生就是单例的。

TODO:

建造者模式

原型模式

结构型模式

前面创建型模式介绍了创建对象的一些设计模式,这节介绍的结构型模式旨在通过改变代码结构来达到解耦的目的,使得我们的代码容易维护和扩展。

代理模式

第一个要介绍的代理模式是最常使用的模式之一了,用一个代理来隐藏具体实现类的实现细节,通常还用于在真实的实现的前后添加一部分逻辑。

既然说是代理,那就要对客户端隐藏真实实现,由代理来负责客户端的所有请求。当然,代理只是个代理,它不会完成实际的业务逻辑,而是一层皮而已,但是对于客户端来说,它必须表现得就是客户端需要的真实实现。

理解代理这个词,这个模式其实就简单了。 下面上代码理解。 代理接口:

//要有一个代理接口让实现类和代理实现类来实现。
public interface FoodService {
    Food makeChicken();
}

被代理的实现类:

public class FoodServiceImpl implements FoodService {
    @Override
    public Food makeChicken() {
        Food f = new Chicken();
        f.setChicken("1kg");
        f.setSpicy("1g");
        f.setSalt("3g");
        System.out.println("鸡肉加好佐料了");
        return f;
    }
}

被代理实现类就只需要做自己该做的事情就好了,不需要管别的。

代理实现类:

public class FoodServiceProxy implements FoodService {
    // 内部一定要有一个真实的实现类,当然也可以通过构造方法注入
    private FoodService foodService = new FoodServiceImpl();
    
    @Override
    public Food makeChicken() {
        System.out.println("开始制作鸡肉");
        
        // 如果我们定义这句为核心代码的话,那么,核心代码是真实实现类做的,
        // 代理只是在核心代码前后做些“无足轻重”的事情
        Food food = foodService.makeChicken();
        
        System.out.println("鸡肉制作完成啦,加点胡椒粉");
        food.addCondiment("pepper");
        System.out.println("上锅咯");
        return food;
    }
}

客户端调用,注意,我们要用代理来实例化接口:

// 这里用代理类来实例化
FoodService foodService = new FoodServiceProxy();
foodService.makeChicken();

所谓代理模式,就是对被代理方法包装或者叫增强, 在面向切面编程(AOP)中,其实就是动态代理的过程。比如 Spring 中,我们自己不定义代理类,但是 Spring 会帮我们动态来定义代理,然后把我们定义在 @Before、@After、@Around 中的代码逻辑动态添加到代理中。

待续。。。

行为型模式

模板模式

在含有继承结构的代码中,模板方法模式是非常常用的。

父类定义了骨架(调用哪些方法及顺序),某些特定方法由子类实现

模板方法只负责定义第一步应该要做什么,第二步应该做什么,第三步应该做什么,至于怎么做,由子类来实现。

好处:代码复用,减少重复代码。除了子类要实现的特定方法,其他方法及方法调用顺序都在父类中预先写好

缺点: 每一个不同的实现都需要一个子类来实现,导致类个数增加,使系统更加庞大

模板模式的关键点:

    1、使用抽象类定义模板类,并在其中定义所有的基本方法、模板方法,钩子方法,不限数量,以实现功能逻辑为主。其中基本方法使用final修饰,其中要调用基本方法和钩子方法,基本方法和钩子方法可以使用protected修饰,表明可被子类修改。

    2、定义实现抽象类的子类,重写其中的模板方法,甚至钩子方法,完善具体的逻辑。

  使用场景:

    1、在多个子类中拥有相同的方法,而且逻辑相同时,可以将这些方法抽出来放到一个模板抽象类中。

    2、程序主框架相同,细节不同的情况下,也可以使用模板方法。

架构方法介绍

模板方法使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤。其主要分为两大类:模版方法和基本方法,而基本方法又分为:抽象方法(Abstract Method),具体方法(Concrete Method),钩子方法(Hook Method)。

四种方法的基本定义(前提:在抽象类中定义):

(1)抽象方法:由抽象类声明,由具体子类实现,并以abstract关键字进行标识。

(2)具体方法:由抽象类声明并且实现,子类并不实现或者做覆盖操作。其实质就是普遍适用的方法,不需要子类来实现。

(3)钩子方法:由抽象类声明并且实现,子类也可以选择加以扩展。通常抽象类会给出一个空的钩子方法,也就是没有实现的扩展。它和具体方法在代码上没有区别,不过是一种意识的区别;而它和抽象方法有时候也是没有区别的,就是在子类都需要将其实现的时候。而不同的是抽象方法必须实现,而钩子方法可以不实现。也就是说钩子方法为你在实现某一个抽象类的时候提供了可选项,相当于预先提供了一个默认配置。

(4)模板方法:定义了一个方法,其中定义了整个逻辑的基本骨架。

public abstract class AbstractTemplate {
    // 这就是模板方法
    public void templateMethod() {
        init();
        apply(); // 这个是重点
        end(); // 可以作为钩子方法
    }
	//这是具体方法
    protected void init() {
        System.out.println("init 抽象层已经实现,子类也可以选择覆写");
    }

    // 这是抽象方法,留给子类实现
    protected abstract void apply();
	//这是钩子方法,可定义一个默认操作,或者为空
    protected void end() {
    }
}

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

莱恩大数据

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值