设计模式六大原则
- 单一职责原则:就一个类而言,应该仅有一个引起它变化的原因
- 开放封闭原则:类、模块、函数等应该是可以拓展的,在拓展时尽量少修改
- 里氏替换原则:所有引用基类的地方必须能透明地使用其子类对象
- 依赖倒置原则:高层模块不应该依赖底层模块,两者都应该依赖于抽象,抽象不应该依赖于细节,细节应该依赖于抽象
- 迪米特原则:一个软件实体应当尽可能少地与其他实体发生相互左右
- 接口隔离原则:一个类对另一个类的依赖应该建立在最小的接口上
设计模式分类
- 创建型:单例、工厂方法、抽象工厂、建造者、原型
- 结构设计型模式:适配器模式、装饰模式、代理模式、外观模式、桥接模式、组合模式、享元模式
- 行为型设计模式:策略模式、模板方法模式、观察者模式、迭代器模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式
创建型设计模式
单例模式
-
定义:保证一个类有且只有一个实例,并提供一个访问的全局访问点
单例模式的6种写法
- 饿汉模式
/**
* 单例模式
* 饿汉模式
*/
class SingletonHungry{
private static SingletonHungry instance = new SingletonHungry();
private SingletonHungry(){}
public static SingletonHungry getInstance(){
return instance;
}
}
这种方式在类加载时就完成了初始化,所以类加载比较慢。但是这种方式基于类加载机制,避免了多线程同步问题。在类加载的时候就完成实例化,没有达到懒加载的效果。会造成内存的浪费
- 懒汉模式(线程不安全)
class SingletonLazyNoSafe(){
private static SingletonLazyNoSafe instance;
private SingletonLazyNoSafe() {
}
public static SingletonLazyNoSafe getInstance() {
if (instance == null) {
instance = new SingletonLazyNoSafe();
}
return instance;
}
}
懒汉式声明了一个静态对象,在用户第一次调用时初始化,节约了资源,但第一次加载时需要实例化。而且在存在线程安全问题
- 懒汉模式(线程安全|双重检查模式DCL)
class SingletonLazySafe {
private static SingletonLazySafe instance = null;
private SingletonLazySafe(){}
public static SingletonLazySafe getInstance() {
if (instance == null) {
synchronized (instance) {
if (instance == null) {
instance = new SingletonLazySafe();
}
}
}
return instance;
}
}
这种写法在getInstance种对Singleton实例进行了两次判空:第一次是为了不必要的同步第二次是在Singleton等于null的时候才创建实例。这里使用volatile会或多或少地影响性能。DCL的优点是资源利用率高,效率高。缺点是第一次加载时反应慢,DCL在某些情况下会出现失效的问题,也就是DCL失效。
- 静态内部类单例模式
class SingletonInner {
private static SingletonInner instance = null;
private SingletonInner() {
}
public static SingletonInner getInstance() {
return SingletonInner.Holder.holderInstance;
}
private static class Holder {
public static SingletonInner holderInstance = new SingletonInner();
}
}
第一次加载Singleton类时并不会初始化holderInstance,只有第一次调用getInstance方法时虚拟机加载SingletonHolder并初始化holderInstance,因此比较推荐使用静态内部类的单例模式
- 枚举单例
enum SingletonEnum {
INSTANCE;
public void someMethod() {
}
}
单例模式的使用场景
- 整个项目需要一个共享访问点或共享数据
- 创建一个对象需要耗费的资源过多,比如IO等
- 工具类
简单工厂模式
简单工厂模式(静态工厂方法模式)
-
定义:由一个工厂对象决定创建出哪一种产品类的实例
简单工厂模式种有如下角色:
- Factory:工厂类,简单工厂模式的核心,它负责实现所有实例的内部逻辑。工厂类创建的产品类的方法可以被外界直接调用,创建所需的产品对象
- IProduct:抽象的产品类,简单工厂模式所创建的所有对象的父类,负责描述所有实例所有共有的公共接口
- Product:具体产品类
简单工厂模式的实现
假设有一个计算机的代工生产商,它目前已经可以代工生产联想计算机了。随着业务的拓展,这个代工生产商还要生产惠普和华硕的计算机。 这样我们就需要用一个单独的类来专门生产计算机,这就用到了简单工厂模式。
/**
* 简单工厂模式
*/
public class SimpleFactoryType {
public static void main(String[] args) {
ComputerFactory.createComputer("hp").start();
}
}
/**
* 抽象产品类
*/
abstract class Computer {
/**
* 产品抽象方法,启动计算机
*/
abstract void start();
}
/**
* 具体产品类
*/
class LenvooComputer extends Computer {
@Override
void start() {
System.out.println("lenovo start");
}
}
class HPComputer extends Computer {
@Override
void start() {
System.out.println("HP start");
}
}
class ASUSComputer extends Computer {
@Override
void start() {
System.out.println("ASUS start");
}
}
class ComputerFactory {
public static Computer createComputer(String type) {
Computer mComputer = null;
switch (type) {
case "lenovo":
mComputer = new LenvooComputer();
break;
case "hp":
mComputer = new HPComputer();
break;
case "asus":
mComputer = new ASUSComputer();
break;
}
return mComputer;
}
}
简单工厂模式的使用场景
- 工厂类负责创建的对象比较少
- 客户只需要知道传入工厂类的参数,无须关心创建逻辑
优点
用户可以根据参数获得对应的类实例。避免直接实例化类,降低了耦合性
缺点
可实例化的类型在编译期间就已经被确定。如果增加新类型,则需要修改工厂,违背了开放封闭原则。简单工厂需要知道所有的要生产的类型
工厂方法模式
-
定义 定义一个用于创建对象的接口,让子类决定实例化哪个类。工厂方法使一个类的实例化延迟到其子类
在工厂方法模式种有如下角色:
- Product:抽象产品类
- ConcreteProduct:具体产品类
- Factory:抽象工厂类,返回抽象产品对象
- ConcreteFactory:具体工厂类,返回具体产品实例
/**
* 工厂方法模式
*/
public class FactoryMethodType {
public static void main(String[] args) {
GDComputerFactor computerFactor = new GDComputerFactor();
computerFactor.createComputerABS(HPComputerABS.class).start();
}
}
/**
* 抽象产品类
*/
abstract class ComputerABS {
/**
* 产品抽象方法,启动计算机
*/
abstract void start();
}
/**
* 具体产品类
*/
class LenvooComputerABS extends ComputerABS {
@Override
void start() {
System.out.println("lenovo start");
}
}
class HPComputerABS extends ComputerABS {
@Override
void start() {
System.out.println("HP start");
}
}
class ASUSComputerABS extends ComputerABS {
@Override
void start() {
System.out.println("ASUS start");
}
}
/**
* 抽象工厂,返回抽象的产品
*/
abstract class ComputerFactoryABS{
public abstract T createComputerABS(Class cls);
}
class GDComputerFactor extends ComputerFactoryABS {
@Override
public T createComputerABS(Class cls) {
ComputerABS computerABS = null;
String clsName = cls.getName();
try {
computerABS = ((ComputerABS) Class.forName(clsName).newInstance());
} catch (IllegalAccessException | InstantiationException | ClassNotFoundException e) {
e.printStackTrace();
}
return (T) computerABS;
}
}
建造者模式
建造者模式也被称为生成器模式,它是创建一个复杂对象的创建型模式,其将构建复杂对象的过程和它的部件解耦。使得构建过程和部件表示分离开来。例如我们要 DIY 一台台式计
算机。 我们找到 DIY 商家。这时我们可以要求这台计算机的 CPU、 主板或者其他部件都是什么牌子的、 什么配置的,这些部件是我们可以根据自己的需求来变化的。 但是这些部件组装成计算机的过程是一样的,我们无须知道这些部件是怎样组装成计算机的,我们只需要提供相关部件的牌子和配置就可以了。对于这种情况我们就可以采用建造者模式,将部件和组装过程分离,使得构建过程和部件都可以自由拓展,两者之间的耦合也降到最低。
-
定义:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示
在建造者模式种有如下角色:
- Director:导演类
- Builder:抽象Builder类,规范产品的组建,一般由子类实现
- ConcreteBuilder:具体建造者,实现Builder类定义的所有方法,返回组建好的对象
- Product:产品类
建造者模式的简单实现
public class BuilderTypeLSN {
public static void main(String[] args) {
ApplePhoneBuilder phoneBuilder = new ApplePhoneBuilder();
Director director = new Director(phoneBuilder);
Phone phone = director.createPhone("aaa", "b", "c");
}
}
class Phone {
private String mCPU;
private String mMainboard;
private String mRam;
public void setmCPU(String mCPU) {
this.mCPU = mCPU;
}
public void setmMainboard(String mMainboard) {
this.mMainboard = mMainboard;
}
public void setmRam(String mRam) {
this.mRam = mRam;
}
}
abstract class Builder{
abstract void buildCPU(String cpuName);
abstract void buildMainboard(String boardName);
abstract void buildRam(String ram);
abstract Phone createPhone();
}
class ApplePhoneBuilder extends Builder {
Phone phone = new Phone();
@Override
void buildCPU(String cpuName) {
phone.setmCPU(cpuName);
}
@Override
void buildMainboard(String boardName) {
phone.setmMainboard(boardName);
}
@Override
void buildRam(String ram) {
phone.setmRam(ram);
}
@Override
Phone createPhone() {
return phone;
}
}
class Director {
Builder mBuilder = null;
public Director(Builder mBuilder) {
this.mBuilder = mBuilder;
}
public Phone createPhone(String cpu, String board, String ram) {
mBuilder.buildCPU(cpu);
mBuilder.buildMainboard(board);
mBuilder.buildRam(ram);
return mBuilder.createPhone();
}
}
使用建造者的场景和优缺点
- 使用场景
- 当创建复杂对象的算法应该独立于该对象的组成部分以及它们的装配方式时。
- 相同的方法,不同的执行顺序,产生不同的事件结果时。
- 多个部件或零件都可以被装配到一个对象中,但是产生的运行结果又不相同时。
- 产品类非常复杂,或者产品类中的调用顺序不同而产生了不同的效能。
- 在创建一些复杂的对象时,这些对象的内部组成构件间的建造顺序是稳定的,但是对象的内部组成构件面临着复杂的变化。
- 优点:
- 使用建造者模式可以使客户端不必知道产品内部组成的细节。
- 具体的建造者类之间是相互独立的,容易扩展。
- 由于具体的建造者是独立的,因此可以对建造过程逐步细化,而不对其他的模块产生任何影响。
- 缺点: 产生多余的 Build 对象以及导演类