浅谈设计模式

设计模式六大原则

单一职责原则

单一职责原则(Single Responsibility Principle, SRP):一个类只负责一个功能领域中的相应职责,或者可以定义为:就一个类而言,应该只有一个引起它变化的原因。

不要使用一个类分担多个职责,影响原有代码逻辑。 增加功能不应该去修改已有代码,而是考虑是否到应该创建一个新的职责类。

例:


// 反例 每次新增业务都需要去修改原有的类
public class DBConection{
 public  static void connetion(String dbName){
     if("Mysql".equals(dbName)){
        System.out.print("连接到 Mysql 数据库") 
     }
      if("Oracle".equals(dbName)){
        System.out.print("连接到 Oracle 数据库") 
     }
   
    .......
    
 }
}

// 正例
public interface DbConnection{
   void connetion();
}

// MysqlConnection 只负责 Mysql 数据库的连接
public class MysqlConnection implements DbConnection{
  @override
  public  void connetion(){
      System.out.print("连接到 Mysql 数据库") 
  }
}

复制代码


里氏替换原则

里氏替换原则(Liskov Substitution Principle, LSP):所有引用基类(父类)的地方必须能透明地使用其子类的对象。

子类在继承父类的时候,可以重写父类的抽象方法和扩展子类方法。不应该去重写父类原有的方法

方法参数,返回值 可定义为基类,具体实现可以用子类传递或返回。



依赖倒转原则

依赖倒转原则(Dependence Inversion Principle) 高层模块不应该依赖底层模块,二者都该依赖其抽象;抽象不应该依赖细节;细节应该依赖抽象。换言之,要针对接口编程,而不是针对实现编程。

例:

集合框架的 Collection 接口


// 所有的集合类都实现了 Collection 接口抽象
public interface Collection<E> extends Iterable<E> {
    int size();
    boolean isEmpty();
    ....
}



复制代码


接口隔离原则

接口隔离原则(Interface Segregation Principle) 类间的依赖关系应该建立在最小的接口上

将接口职责细分,防止过多的空实现。

但也需要注意防止接口细粒度过小造成接口泛滥。

例:


 public interface DataHandler {
     // 转换为Xml
     XmlObject convertToXml();
     // 转换为json
    JsonObject convertToJson();
     // 初始化 Excel
    Excel initExcel();
     
 } 
 
 // 如果我此时只需要 实现 convertToXml convertToJson 的方法,实现DataHandler时必须实现initExcel 空方法
 
  public interface DataHandler{
     // 转换为Xml
     XmlObject convertToXml();
     // 转换为json
    JsonObject convertToJson();
 } 
 
 
 public interface ExcelHandler{
     // 转换为json
     Excel initExcel();
 } 

复制代码


迪米特法则

迪米特法则(Law of Demeter) 一个类对自己依赖的类知道的越少越好

尽量减少两个对象之间的交互,如果两个对象之间不必直接通信。那么两个对象就不应该发生任何的直接作用。可以通过第三者转发这个歌调用。简单来说就是引用一个第三者来降低系统的耦合。直接与其他类通信必然会造成和其他有耦合关系,例如: 组合 继承。 在多个类具有相互直接关系的类之间,增加一个中间类(Mediator) 调用者不用与实现类产生耦合关系,只需要关心调用的方法即可。



开闭原则

开闭原则(Open-Closed Principle, OCP):一个软件实体应当对扩展开放,对修改关闭。

简介:

在软件的生命周期内,因为变化、升级和维护等原因需要对软件原有代码进行修改时,可能会给旧代码中引入错误,也可能会使我们不得不对整个功能进行重构,并且需要原有代码经过重新测试,整个流程对开发周期影响很大,这个时候就需要开闭原则来解决这种问题


复合模式 (附加)

尽量使用组合与聚合代替继承


设计模式

创建型设计模式

简单工程模式 Simple Factory

简单工厂模式又称静态工程方法,由一个工厂对象决定创建出哪一种产品类的实例,简单工厂模式不属于23种GOF设计模式之一。


public class AnimalFactory{

   public static Animal getAnimal(String name){
       if("tiger".equals(name){
           return new Tiger();
       }
       if("dolphin".equals(name)){
           return new Dolphin();
       }
       
       .....
        return null;
   } 
}

// 简单工厂生产
Animal tiger = AnimalFactory.getAnimal("tiger");

复制代码

工厂模式 Factory Model

一个工厂类负责生产一种标准的产品 符合设计模式单一职责原则和依赖倒转原则并且依赖于抽象


// 首先我们需要一个玩具工厂接口制定生产标准
public interface ToyFactory{
    // 生产玩具
    public static Toy product();
}

// IcePrincess Audi 都派生自 Toy 接口
public class LegoFactory implements ToyFactory{
    
    @override
    public static Toy product(){
        return new IcePrincess();
    }
    
}

public class CarFactory implements ToyFactory{
    
    @override
    public static Toy product(){
        return new Audi();
    }
    
}

// 选择一个实际我们需要的工厂,开始生产玩具
pbucli class demo{
    public static void main(String[] args){
     Car car  = new CarFactory.product();
     IcePrincess icePrincess  = new LegoFactory.product();
    }
}

复制代码

抽象工厂模式 Abstract Factory

抽象工厂模式是指当有多个抽象角色时,使用的一种工厂模式。抽象工厂模式可以向客户端提供一个接口,使客户端在不必指定产品的具体的情况下,创建多个产品族中的产品对象。仅仅是类型与这些抽象产品角色相同的一些实例,而不是这些抽象产品的实例。换言之,也就是这些抽象产品的具体子类的实例。工厂类负责创建抽象产品的具体子类的实例。

例:

优势:一个产品族的工程对象即可创建出符合统一标准的所有产品。不用重复创建多个工程生产多个产品最后进行组装。
劣势:违反开闭原则,当产品族创建新的产品时所有继承抽象工厂的工程类都必须进行代码重构。
复制代码

// 惠普电脑生产工厂
public class HPComputerFactory extends AbstractComputerFactory{

  public static HPComputer createComputer(){
    InterCpu cpu = this.crteateCpu();
    SamsungMemory memory = this.createMemory();
    return new HPComputer(cpu, memory)
  }
  
   public static Cpu crteateCpu(){
     return InterCpuFactory.createCpu();
   }
   
   public static Merroy createMemory(){
     return SamsungFactory.createMemory();
   }
} 

复制代码

备注:工厂模式用于封装复杂的对象创建逻辑,只需要对使用者暴露创建接口即可。


建造者模式 Builder

适用于属性特别多的对象,采用链式编程简化对象的创建

// 简单的建造者模式

class User {
   
    private String name;
    private String password;
    private String nickName;
    private int age;

    
    private User(String name, String password, String nickName, int age) {
        this.name = name;
        this.password = password;
        this.nickName = nickName;
        this.age = age;
    }
        // 链式调用设置各个属性值,返回 this,即 UserBuilder
        public UserBuilder name(String name) {
            this.name = name;
            return this;
        }

        public UserBuilder password(String password) {
            this.password = password;
            return this;
        }

        public UserBuilder nickName(String nickName) {
            this.nickName = nickName;
            return this;
        }

        public UserBuilder age(int age) {
            this.age = age;
            return this;
        }
}

复制代码
// 也可以使用 Lombok 简化代码

@Builder
class User {
    private String name;
    private String password;
    private String nickName;
    private int age;
    }
复制代码

核心是:先把所有的属性都设置给 Builder,然后 build() 方法的时候,将这些属性复制给实际产生的对象。

原型模式 Prototype

基于原有对象产生新的对象实例

  • Object.copy() 浅拷贝

    需要被copy的对象实现Cloneable接口,对象内部的对象拷贝的是同一引用。
    复制代码
  • 序列化与反序列化 深拷贝

    如果采用了JDK自带的序列化,需要实现 Serializable接口。然后进行反序列化 缺点:效率低

单例模式 Singleton

  • 饱汉式
 // 饱汉式缺点:当单例对象没有实例化的时候,Class 加载的时候就已经为此对象分配了内存空间
 public class Sigleton{
 
   // 私有的构造方法,防止外部通过构造方法实例化
   private Sigleton {}
   
   private Sigleton sigleton = new Sigleton();
   
   public static Sigleton getInstance(){
    return sigton;    
   } 
 }
 
复制代码
  • 饿汉式(线程安全)
// 双重检查 防止大量线程在synchronized代码块进行等待,造成锁升级。效率低
public class Sigleton{

    // 私有构造方法
    private Sigleton {}
    // volatitle 保证对象在多个线程的可见性
    private static volatitle Sigleton sigleton = null;
    
    public static Sigleton getInstance(){
    
    if(sigleton == null){
       synchronized(Sigleton.class){
          if(sigleton; == null){
           sigleton = new Singleton();
       }
     }
    }
    return new Sigleton();
}

// 嵌套类 利用嵌套类的属性 第一次被引用的时候才被加载
public class Sigleton{
// 私有构造方法
    private Sigleton {}
    
    private static class SigletonHolder{
         private final static Singleton instance=new Singleton();
    }
    
     public static Singleton getInstance(){
        return SingletonHolder.instance;
    }

}


复制代码
结构型设计模式

适配器 Adapter

 将一个类的接口转换成客户希望的另一个接口。适配器模式让那些接口不兼容的类可以一起工作。


 类比: (两脚充电器 )—-> 转换头 --> (三脚充电器)

 目标类:定义客户所需要的接口或抽象类 三脚充电器 

 适配类:作为转换器 对目标类和适配者类进行适配 转换头

 适配者类:作为被适配对象,包装好了主要执行的业务逻辑 两脚充电器
复制代码
  • 类适配
// 适配者类
public interface TwoPinPlug{
    void charge();
}

 // 目标类
public class ThreePinPlug{
   public void charge(){
       ....
   }
}

// 适配类
public class PinPlugAdapter extends ThreePinPlug implements TwoPinPlug{
      public void charge(){
       ....
       }
}


复制代码
  • 对象适配
 // 适配者类
 public class TwoPinPlug{
    public void charge(){
         // 充电的主要业务逻辑
         .....
     }
 }
  
  // 目标类
  public interface ThreePinPlug {
       public void charge();
  }
 
 // 适配类
 public class PinPlugAdapter implements ThreePinPlug{
 
    TwoPinPlug twoPingPlug = null;
    
    public PinPlugAdapter(TwoPinPlug twoPingPlug){
      this.twoPingPlug = twoPingPlug;
    }
    
    TwoPinPlug twoPinPlug = new  TwoPinPlug();
    
    public void charge(){
        twoPinPlug.charge();
     }
     
 }
 
 
 
 
  // 用转换头装配好的充电器进行充电
 ThreePinPlug adapter = new PinPlugAdapter();
 adapter.charger();
 
复制代码

优势:将目标类与适配类解耦,增加了类的复用性,将业务逻辑封装在适配类中,同一个适配者类可在不同环境中使用。

缺点:java是单继承,所以适配器的适配者只是是一个。


桥接模式 Bridge

将抽象部分与它的实现部分分离,使它们都可以独立地变化。

例:

 举一个形象的例子。当我们在画画的时候。需要三种粗细六种颜色的蜡笔。那么我们需要准备18支蜡笔。而同样的情况
 如果我们选择了油彩笔和六种颜色的颜料就可以随意组合出你需要的所有画笔。如果粗细和颜色有变化只要新增即可
 但使用蜡笔我们则需要准备更多的不同粗细和颜色的蜡笔。这就是桥接模式最生动的演示,当我们在软件开发时,某一
 个类存在两个独立变 的维度时,通过桥接模式,可以将这两个维度分离出来,使两者可以单独扩展变化,让系统更符合
 “单一职责”原则。
复制代码

// 笔
public abstract class Pen{

  // 写
  public abstract void draw();
    
}

// 颜料
public interface Color{

    
    
}





复制代码

组合模式 Composite

装饰器模式 Decorator

门面模式 Facade

代理模式 Proxy

当目标对象的功能不足以满足客户端的要求,系统为该对象创建一个代理对象,而代理对象可以增强原目标对象的功能

  • 静态代理

// 代理类与被代理类实现同一个接口,代理类持有被代理类引用

public interface Light {
    // 照明
    public void lighting();
}

// 普通电灯
public class Lamp implements Light {

 @Override
 public void lighting(){
     ...........
  }
}

public class LampProxy implements Light{

 // 代理类持有被代理类的引用
 private Lamp lamp;
 
 public  class LampProxy(Lamp lamp){
     this.lamp = lamp
 }
 
 @Override
 public void lighting(){
    // 发亮
    lamp.lighting();
     // 加大瓦数
    incrPowere();
    // 变换色彩
    changeColor();
   }
 }
 
 
 Lamp lamp = new LampProxy(lamp)
 lamp.lighting();
 
复制代码
  • 动态代理 (JDK)

动态代理实现了解耦,通过使用动态代理,程序就为被代理对象增加了额外功能。

  1. 实现 InvocationHandler接口

  2. Proxy.newProxyInstance() 创建动态代理对象


publi class LampHandler implements InvocationHandler {
    
    // 被代理对象
    private Object target;
    
    public void setTarget(Object target){
        this.target = target;
    }
    
    // 执行动态代理对象的所有方法,都会被替换成执行如下方法
    public Object invoke(Object proxy, Method method, Object[] args){
      // do something ........
      Object result method.invoke(target, args)
      // do something ........  
      return result;
    }
    
}

public class MyProxyFactory{

    public static Object getProxy(Object target){
    
        LampHandler lampHandler = new LampHandler();
        
        // 为LampHandler设置Target值
        handler.setTarget(target);
        
        // 创建并返回一个动态代理
        return Proxy.newProxyInstance(target.getclass.getClassLoader(), target.getClass.getInterfaces(), handeler)
    }
}



复制代码
  • 动态代理(CGLib)

TODO

行为型设计模式

模板方法模式(Template Method)

责任链模式(Chain of Responsibility)

迭代器模式(Iterator)

观察者模式(Observer)

策略模式(Strategy)

转载于:https://juejin.im/post/5c77fd0551882532cd57a004

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值