设计模式
1.设计模式的定义
- 1.设计模式(
Design Pattern):是对代码开发经验的总结,是解决特定问题的一系列方案- 2.其不是语法规定,而是一套用来提高代码可复用性、可维护性、可读性、稳健性以及安全性的解决方案
2.设计模式的六大原则
开闭原则(Open Closed Principle)
- 1.一个软件实体(类,模块和函数)应该对扩展开放,对修改关闭
- 2.即程序需要进行拓展的时候,不能去修改原有的代码,而是要扩展原有代码,从而实现一个热插拔的效果
- 3.
目的:为了使程序的扩展性好,易于维护和升级- 4.
实现:使用接口和抽象类等
1.单一职责原则(Single Responsibility Principle)
- 1.一个类应该只有一个发生变化的原因,不要存在多于一个导致类变更的原因
- 2.即每个类应该实现单一的职责,否则就应该把类拆分
2.里氏替换原则(Liskov Substitution Principle)
- 1.所有引用
基类的地方必须能透明地使用其子类的对象- 2.任何基类可以出现的地方子类一定可以出现,
里氏替换原则是继承复用的基石,只有当子类可以替换基类,软件单位的功能不受到影响时,基类才能真正被复用,而子类也能够在基类的基础上增加新的行为- 3.
里氏代换原则是对开闭原则的补充,实现开闭原则的关键步骤就是抽象化,而基类与子类的继承关系就是抽象化的具体实现,所以里氏替换原则是对实现抽象化具体步骤的规范- 4.
里氏替换原则中子类对父类的方法尽量不要重写和重载,因为父类代表了定义好的结构,通过这个规范的接口与外界交互,子类不应该随便破坏它
3.依赖倒置原则(Dependence Inversion Principle)
- 1.上层模块不应该依赖底层模块,而应该依赖于抽象
- 2.抽象不应该依赖于细节,细节应该依赖于抽象
- 3.即面向接口编程,依赖于抽象而不依赖于具体类,写代码时用到具体类时,不与具体类交互而是与具体类的上层接口交互
4.接口隔离原则(Interface Segregation Principle)
- 1.实体类不应该依赖它不需要的接口
- 2.类间的依赖关系应该建立在最小的接口上
- 3.每个接口中不存在子类用不到却必须实现的方法,否则将接口拆分,使用多个隔离的接口,比使用单个接口(多个接口方法集合到一个的接口)要好
5.迪米特法则(最少知道原则)(Law of Demeter)
- 1.一个类对自己依赖的类知道的越少越好,无论被依赖的类多么复杂,都应该将逻辑封装在方法的内部,通过
public方法提供给外部,这样当被依赖的类变化时才能最小的影响该类- 2.即一个实体应当尽量少的与其他实体之间发生相互作用使得系统功能模块相对独立
6.合成复用原则(Composite Reuse Principle)
- 1.尽量使用对象组合/聚合,而不是继承关系达到软件复用的目的
- 2.合成或聚合可以将已有对象纳入到新对象中使之成为新对象的一部分,因此新对象可以调用已有对象的功能
3.设计模式的三大类
1.创建型模式(Creational Pattern)
- 1.对类的实例化过程进行了抽象,能够将软件模块中对象的创建和对象的使用分离(
5种)
- 1.
工厂模式- 2.
原型模式- 3.
单例模式- 4.
建造者模式- 5.
抽象工厂模式- 2.记忆口诀:创工原单建抽(创公园,但见愁)
1.工厂模式(Factory Pattern)
- 1.定义一个创建对象的接口,让其子类决定实例化具体的工厂类,工厂模式使其创建过程延迟到子类进行
- 2.创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象
1.普通工厂模式
- 1.建立一个工厂类并对实现了同一接口的子类进行实例的创建
//例:发送邮件和短信 //1.首先,创建二者的共同接口 public interface Sender { public void Send(); } //2.其次,创建实现类 public class MailSender implements Sender { @Override public void Send() { System.out.println("this is mailsender!"); } } public class SmsSender implements Sender { @Override public void Send() { System.out.println("this is sms sender!"); } } //最后,建工厂类 public class SendFactory { public Sender produce(String type) { if ("mail".equals(type)) { return new MailSender(); } else if ("sms".equals(type)) { return new SmsSender(); } else { System.out.println("请输入正确的类型!"); return null; } } } //测试 public class FactoryTest { public static void main(String[] args) { SendFactory factory = new SendFactory(); Sender sender = factory.produce("sms"); sender.Send(); } }2.多工厂模式
- 1.普通工厂模式中如果传递的字符串出错则不能正确创建对象
- 2.多工厂模式是对普通工厂模式的改进,提供多个工厂方法分别创建对象
//例:发送邮件和短信 //1.首先,创建二者的共同接口 public interface Sender { public void Send(); } //2.其次,创建实现类 public class MailSender implements Sender { @Override public void Send() { System.out.println("this is mailsender!"); } } public class SmsSender implements Sender { @Override public void Send() { System.out.println("this is sms sender!"); } } //最后,建工厂类 public class SendFactory { public Sender produceMail(){ return new MailSender(); } public Sender produceSms(){ return new SmsSender(); } } //测试 public class FactoryTest { public static void main(String[] args) { SendFactory factory = new SendFactory(); Sender sender = factory.produceMail(); sender.Send(); } }3.静态工厂方法模式
- 1.将多工厂模式中的方法置为静态的,不需要创建实例就可以直接调用,并将其构造方法私有
//例:发送邮件和短信 //1.首先,创建二者的共同接口 public interface Sender { public void Send(); } //2.其次,创建实现类 public class MailSender implements Sender { @Override public void Send() { System.out.println("this is mailsender!"); } } public class SmsSender implements Sender { @Override public void Send() { System.out.println("this is sms sender!"); } } //最后,建工厂类 public class SendFactory { public static Sender produceMail(){ return new MailSender(); } public static Sender produceSms(){ return new SmsSender(); } private SendFactory(){ }; } //测试 public class FactoryTest { public static void main(String[] args) { Sender sender = SendFactory.produceMail(); sender.Send(); } }- 1.
工厂模式应用场景:需要创建大量的对象- 2.
工厂模式采用方案:以上三种模式,一般采用静态工厂方法模式- 3.
工厂模式实际案例:
- 1.
Executors类中返回线程池的方法
2.原型模式(Prototype Pattern)
- 1.该模式的是将一个对象作为原型,对其进行复制、克隆,产生一个和原对象类似的新对象
- 2.原型模式实现克隆操作,需要实现
Cloneable接口并重写clone()抽象方法- 3.深拷贝和浅拷贝:可参考
JAVA SE文章中的深拷贝和浅拷贝//抽象类实现克隆接口(Cloneable) public abstract class Shape implements Cloneable { private String id; protected String type; abstract void draw(); public String getType(){ return type; } public String getId() { return id; } public void setId(String id) { this.id = id; } public Object clone() { Object clone = null; try { clone = super.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); } return clone; } } //扩展抽象类的实体类 public class Rectangle extends Shape { public Rectangle(){ type = "Rectangle"; } @Override public void draw() { System.out.println("Inside Rectangle::draw() method."); } } public class Square extends Shape { public Square(){ type = "Square"; } @Override public void draw() { System.out.println("Inside Square::draw() method."); } } public class Circle extends Shape { public Circle(){ type = "Circle"; } @Override public void draw() { System.out.println("Inside Circle::draw() method."); } } //创建一个类,从数据库获取实体类,并把它们存储在一个 Hashtable 中。 import java.util.Hashtable; public class ShapeCache { private static Hashtable<String, Shape> shapeMap = new Hashtable<String, Shape>(); public static Shape getShape(String shapeId) { Shape cachedShape = shapeMap.get(shapeId); return (Shape) cachedShape.clone(); } // 对每种形状都运行数据库查询,并创建该形状 // shapeMap.put(shapeKey, shape); // 例如,我们要添加三种形状 public static void loadCache() { Circle circle = new Circle(); circle.setId("1"); shapeMap.put(circle.getId(),circle); Square square = new Square(); square.setId("2"); shapeMap.put(square.getId(),square); Rectangle rectangle = new Rectangle(); rectangle.setId("3"); shapeMap.put(rectangle.getId(),rectangle); } } //使用ShapeCache 类来获取存储在 Hashtable 中的形状的克隆。 public class PrototypePatternDemo { public static void main(String[] args) { ShapeCache.loadCache(); Shape clonedShape = (Shape) ShapeCache.getShape("1"); System.out.println("Shape : " + clonedShape.getType()); Shape clonedShape2 = (Shape) ShapeCache.getShape("2"); System.out.println("Shape : " + clonedShape2.getType()); Shape clonedShape3 = (Shape) ShapeCache.getShape("3"); System.out.println("Shape : " + clonedShape3.getType()); } }- 1.
原型模式应用场景:需要创建复杂对象或生成大量同一类型对象的时候可以使用原型模式,可以简化对象的创建过程- 2.
原型模式实际案例:
- 1.
Spring中原型Bean的创建
3.单例模式(Singleton Pattern)
- 1.
单例模式涉及一个单一的类,该类负责创建自己的对象同时确保只有单个对象被创建- 2.
单例模式提供了一种访问其唯一对象的方式,可以直接访问不需要实例化该类的对象- 3.
单例模式保证了程序中只有一个实例存在并且能全局访问- 4.注意
- 1.单例类只能有一个实例
- 2.单例类必须自己创建自己的唯一实例
- 3.单例类必须给所有其他对象提供这一实例
- 5.优点
- 1.内存中只有一个实例,减少了内存的开销,尤其是频繁的创建和销毁实例
- 2.避免对资源的多重占用
- 6.缺点
- 1.没有接口,不能继承,与单一职责原则冲突,一个类应该只关心内部逻辑,而不关心外面怎么样来实例化
- 7.作用:保证一个类仅有一个实例,并提供一个访问它的全局访问点
- 8.关键:
构造函数私有化public class SingleObject { //创建 SingleObject 的一个对象 private static SingleObject instance = new SingleObject(); //让构造函数为 private,这样该类就不会被实例化 private SingleObject(){ } //获取唯一可用的对象 public static SingleObject getInstance(){ return instance; } public void showMessage(){ System.out.println("Hello World!"); } } public class SingletonPatternDemo { public static void main(String[] args) { //不合法的构造函数 //编译时错误:构造函数 SingleObject() 是不可见的 //SingleObject object = new SingleObject(); //获取唯一可用的对象 SingleObject object = SingleObject.getInstance(); //显示消息 object.showMessage(); } }1.懒汉式1
- 1.
懒汉式是最基本的实现方式,并没有在加载时就创建单例对象,而是在使用时才去进行懒加载- 2.该方式最大的问题是不支持多线程,因为没有加锁,所以严格意义上并不算单例模式,一般使用适用于单线程
public class Singleton { private static Singleton instance; private Singleton (){ } public static Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } }2.懒汉式2
- 1.该方式线程安全,能应用于多线程中,但是效率低,因为通过
synchronized加锁- 2.优点:第一次调用才初始化,避免内存浪费。
- 3.缺点:必须通过
synchronized加锁才能保证多线程下单例,但加锁会影响效率public class Singleton { private static Singleton instance; private Singleton (){ } public static synchronized Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } }3.饿汉式
- 1.
饿汉式也是最基本的实现方式,在类加载时就创建唯一对象,容易产生垃圾对象浪费内存- 2.优点:基于
类加载机制(可参考JVM文章中的类加载机制)避免了多线程的同步问题,不需要加锁,执行效率会提高- 3.缺点:类加载时就初始化,浪费内存
public class Singleton { private static Singleton instance = new Singleton(); private Singleton (){ } public static Singleton getInstance() { return instance; } }4.双重校验锁(double-checked locking,DCL)
- 1.上述
饿汉式不能延时加载,懒汉式有性能问题,而双重检测方式既支持延迟加载,又支持高并发- 2.当
instance对象被创建后,再次调getInstance方法不再会进入synchronized加锁的代码之中- 3.优点:资源利用率高,第一次执行
getInstance时才会被实例化,并且只有第一次执行才会进行加锁代码中,效率高- 4.缺点:第一次加载反应稍慢
public class Singleton { private static Singleton instance; private Singleton (){ } public static Singleton getInstance() { if (singleton == null) { synchronized (Singleton.class) { if (singleton == null) { singleton = new Singleton(); } } } return instance; } }5.静态内部类
- 1.
静态内部类能达到和双重校验锁一样的功效,但实现更简单- 2.该方式同样利用
类加载机制避免了多线程的同步问题- 3.第
3种方式只要Singleton类被加载,那么instance就会被实例化,而静态内部类中即使Singleton类被加载,instance也不会立即被初始化,因为SingletonHolder类没有被主动使用,只有通过显式调用getInstance方法时才会显式加载SingletonHolder类从而实例化instance- 4.优点:不仅能保证线程安全,也能够保证单例对象唯一,同时也延迟了单例的实例化,而且第一次加载不会慢
public class Singleton { private static class SingletonHolder { private static final Singleton INSTANCE = new Singleton(); } private Singleton (){ } public static final Singleton getInstance() { return SingletonHolder.INSTANCE; } }6.枚举
- 1.
枚举是单例最简单的实现方式,这种实现方式通过Java枚举类型本身的特性,保证了实例创建的线程安全性和实例的唯一性- 2.该方式是
Josh Bloch提倡的方式,不仅能避免多线程同步问题,而且还自动支持序列化机制,防止反序列化重新创建新的对象,绝对防止多次实例化public enum Singleton { INSTANCE; public void whateverMethod() { } }- 1.
单例模式应用场景:只存在一个实例且实例对象需要频繁被访问或一个全局使用的类频繁地创建与销毁- 2.
单例模式采用方案
- 1.以上总结为
五种模式,一般情况下不建议使用第1种和第2种懒汉方式,建议使用第3种饿汉方式- 2.只有在要明确实现懒加载效果时才会使用第
5种静态内部类方式实现懒汉式加载- 3.如果涉及到
反序列化创建对象时,可以使用第6种枚举方式,如果有其他特殊的需求,可以考虑使用第4种双检锁方式- 3.
单例模式实际案例:
- 1.
Java中的Runtime类
- 2.
Spring Bean采用了双重校验锁以及ConcurrentHashMap作为容器实现了单例设计,并且通过三级缓存解决循环依赖的问题
- 3.网站的计数器
4.建造者模式(Builder Pattern)
- 1.
建造者模式是按照指定的步骤一步一步构建复杂对象- 2.使用
建造者模式将原本复杂的对象创建过程按照规律分解成多个小步骤,构建对象时可以灵活的选择或修改步骤从而构建出不同的对象- 3.调用者只需要传入需要构建的类型便能够得到需要的对象,并不需要关心创建的过程从而实现解耦
- 4.优点
- 1.
建造者模式将类的构造函数中的可选参数分离出来,使用setter的方式进行初始化,非常灵活- 2.
建造者模式是链式调用,属性连续设置,当调用build()方法实例化对象后对象不可再进行改变,看起来是一个整体,较简洁,易于程序员阅读和管理- 5.缺点: 由于构造函数私有,使用
build方法构造对象后对象不可变- 6.注意:与工厂模式的区别是:建造者模式更加关注与零件装配的顺序
- 7.实现步骤
- 1.需构建类中中创建一个静态内部类
Builder,然后将构建类中的参数都复制到Builder类中- 2.需构建类中创建一个
private的构造函数,参数为Builder类型- 3.
Builder中创建一个public的构造函数,参数为需构建类中必填的参数- 4.
Builder中创建setter函数,对需构建类中可选参数进行赋值,返回值为Builder类型的实例- 5.
Builder中创建一个build()方法并在其中构建需构建类的实例(参数为Builder当前对象)并返回package com.redis.entity; import lombok.Data; @Data public class Computer { private final String cpu;//必须 private final String ram;//必须 private final int usbCount;//可选 private final String keyboard;//可选 private final String display;//可选 private Computer(Builder builder){ this.cpu=builder.cpu; this.ram=builder.ram; this.usbCount=builder.usbCount; this.keyboard=builder.keyboard; this.display=builder.display; } public static class Builder{ private String cpu;//必须 private String ram;//必须 private int usbCount;//可选 private String keyboard;//可选 private String display;//可选 public Builder(String cup,String ram){ this.cpu=cup; this.ram=ram; } public Builder setUsbCount(int usbCount) { this.usbCount = usbCount; return this; } public Builder setKeyboard(String keyboard) { this.keyboard = keyboard; return



最低0.47元/天 解锁文章
5万+

被折叠的 条评论
为什么被折叠?



