设计模式
设计模式的原则
首先需要了解使用设计模式的目的:
-
代码可重用性:对于功能重复的代码不用多次编写;
-
可读性:使编程具有规范性,便于阅读和理解;
-
可扩展性:在需要添加功能的时候,可以很方(可维护性高);
-
可靠性:当添加新功能之后对原来的功能没有影响;
-
实现高内聚、低耦合。
设计模式的七大原则:
-
单一职责原则
降低类的复杂性,一个类只负责一个职责;
提高类的可读性,可维护性;
降低代码变更的风险;
-
接口隔离原则
客户端不应该它不需要的接口,即一个类对另外一个类的依赖应该建立在最小接口上。
-
依赖倒置原则
面向接口编程;高层模块不依赖低层模块,二则依赖其抽象;抽象不依赖细节,细节因该依赖抽象;抽象的类更加稳定,细节类(实现类)更加多变。依赖传递方式:接口、构造方法、setter方法。
-
里氏替换原则
子类继承父类可以扩展父类的功能,但尽量不要修改父类的方法的功能。如一定要修改可以使用依赖、聚合、组合等关系来处理二者之间的关系。
-
开闭原则
OCP(open close principle)原则是最重要的原则:对于添加或修改一个功能,提供方可以扩展(开放),使用方不能修改(关闭)。
-
迪米特原则
又称最少直到原则,一个类对其依赖的类其内部的了解越少越好。以减少耦合。陌生类最好不要以方法的局部变量方式出现。突出一个封装,将自己的功能封装到自己的类中。
-
合成复用原则
能使用聚合、组合、依赖等,就不使用继承。
UML类图
设计模式的分类
- 创建型模式
- 结构型模式
- 适配器模式
- 桥接模式
- 装饰模式
- 组合模式
- 外观模式
- 享元模式
- 代理模式
- 行为型模式
- 模板方法模式
- 命令模式
- 访问者模式
- 迭代器模式
- 观察者模式
- 中介者模式
- 备忘录模式
- 解释器模式
- 状态模式
- 策略模式
- 责任链模式
创建型模式
单例模式
单例模式(Singleton Pattern):涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。
特点:
- 构造方法私有化
- 类的实例在类中自己自己创建
- 该类的实例通过该类的方法获得
优点:对于相同且频繁创建和销毁的独享,避免多次创建对象,减小内存开销;
缺点:没有接口也不能继承。
具体分类:
饿汉模式
》》在加载类的时候就创建了对象。
优点:简单,在类加载的时候九完成实例化,避免多线程问题;
缺点:没有懒加载,如果在程序的整个运行期间没有使用到,就会浪费内存;
// 饿汉式
class Singleton{
// 私有化构造方法
private Singleton(){ }
// 类中创建实例
private final static Singleton instance = new Singleton();
// 外部通过该方法获得该类的实例
public static Singleton getInstance(){
return instance;
}
}
懒汉式(线程不安全)
》》在调用方法的时候才会创建对象。
优点:简单 、使用懒加载,不会造成资源浪费
缺点:线程不安全(if判断时候被抢占就会创建多个实例)
// 懒汉式
class Singleton{
// 构造方法私有化
private Singleton(){}
// 内部实例
private static Singleton instance = null;
// 对外方法获得该类的实例
public static Singleton getInstance(){
// 在调用该方法(使用到该类)的时候才创建对象
if(instance == null){
instance = new Singleton();
}
return instance;
}
}
懒汉式(线程安全)
》》在方法上添加同步synchronized
优点:简单、使用懒加载,不会造成资源浪费,使用方法同步:线程安全
缺点:效率低
// 懒汉式(线程安全)
class Singleton{
// 构造方法私有化
private Singleton(){}
// 内部实例
private static Singleton instance = null;
// 对外方法获得该类的实例(使用同步)
public synchronized static Singleton getInstance(){
// 在调用该方法(使用到该类)的时候才创建对象
if(instance == null){
instance = new Singleton();
}
return instance;
}
}
双重检查(双检锁)
》》使用两次判断以减小同步代码的范围以提高效率(如果已经创建直接返回即可,不需要再同步)
优点:解决懒加载和同步问题,效率高,推荐使用
// 双检锁
class 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;
}
}
静态内部类
》》使用静态内部类来创建实例对象(外部类被加载时,静态内部类不会被加载;当使用静态内部类的时候,静态内部类只会被加载一次),
优点:懒加载,效率高、线程安全,推荐使用。
// 静态内部类实现
class Singleton{
private Singleton(){}
// 静态内部类
private static class SingletonInstance{
// 内部类属性创建对象
private static final Singleton INSTANCE = new Singleton();
}
// 获取实例
public static Singleton getInstance(){
return SingletonInstance.INSTANCE;
}
}
枚举
使用枚举类实现单例
优点:线程安全,支持反序列化机制,绝对防止多次创建,最佳方法。
enum Singleton{
INSTANCE;
public void say(){}
}
JDK中式单例模式:
比如Runtime类:(饿汉式)
public class Runtime {
private static Runtime currentRuntime = new Runtime();
/**
* Returns the runtime object associated with the current Java application.
* Most of the methods of class <code>Runtime</code> are instance
* methods and must be invoked with respect to the current runtime object.
*
* @return the <code>Runtime</code> object associated with the current
* Java application.
*/
public static Runtime getRuntime() {
return currentRuntime;
}
/** Don't let anyone else instantiate this class */
private Runtime() {}
// other methods。。。。。。。。。。。
}
工厂模式
在工厂模式(Factory Pattern)中,我们在创建对象时不会对客户端暴露创建逻辑,只需要根据客户的需求生产出对应的对对象并返回即可。适用于生产一种类型的物品(java中的类)。
简单工厂模式(静态工厂模式)
创建一个工厂,通过该工厂来创建实例对象并返回。如下:
- 需求:生产不同品牌的手机
- Phone为抽象类,有抽象方法generate();
- xiaom、huawei、apple继承Phone;
- SimpleFactory为工厂类,有静态方法create()创建对象
- Order类作为客户端,调用工厂类得到对象。
抽象类:
/**
* 抽象类
*/
public abstract class Phone {
public abstract void generate();
}
实现类:
/**
* xiaomi
*/
public class XiaoMi extends Phone{
@Override
public void generate() {
System.out.println("XIAOMI phone is generate ");
}
}
/**
* huawei
*/
public class HuaWei extends Phone{
@Override
public void generate() {
System.out.println("HUaWei phone is generate");
}
}
/**
* apple
*/
public class Apple extends Phone{
@Override
public void generate() {
System.out.println("Apple phone is generate");
}
}
工厂类:
/**
* 工厂类
*/
public class SimpleFactory {
private static Phone phone = null;
// 根据传入名称返回对象
public static Phone createPhone(String phoneType){
switch (phoneType){
case "xiaomi":
phone = new XiaoMi();
phone.generate();
break;
case "huawei":
phone = new HuaWei();
phone.generate();
break;
case "apple":
phone = new Apple();
phone.generate();
break;
default:
break;
}
return phone;
}
}
Order客户端:
public class Order {
public static void main(String[] args) {
Phone phone1 = SimpleFactory.createPhone("xiaomi");
Phone phone2 = SimpleFactory.createPhone("huawei");
Phone phone3 = SimpleFactory.createPhone("apple");
}
}
抽象工厂模式
在抽象工厂模式中,接口是负责创建一个相关对象的工厂,不需要显式指定它们的类。具体的工厂实现与该接口并具体化其方法。
抽象工厂区别于简单工厂模式:简单工厂只需完成成产生同一个类型的产品;但是当一个大的类中分了若干小的子类产品时,一个工厂就不能完成了,就需要更多具体的工厂来实现。
如下:
- 需求:某公司生产电子产品(Electronic products),包括:手机、电脑。
- Computer为电脑抽象类;Phone为手机抽象类;
- AbstractFactory为抽象类工厂;ComputerFactory、PhoneFactory为其子类工厂;
- FactoryProducer为工厂选择器,通过该类选择具体的工厂
- Order为客户端。
Phone:
同简单工厂模式中的Phone;
Computer:
与Phone类似,改名字而已。
工厂类:
/**
* 抽象工厂类
*/
public abstract class AbstractFactory {
public abstract <T> T create(String s);
}
/**
* 电脑工厂类
*/
public class ComputerFactory extends AbstractFactory{
private Computer computer;
@Override
public Computer create(String computerType) {
switch (computerType){
case "lianxiang":
computer = new LianXiang();
computer.generateComputer();
break;
case "huasuo":
computer = new HuaSuo();
computer.generateComputer();
break;
default:
break;
}
return computer;
}
}
/**
* 手机工厂类
*/
public class PhoneFactory extends AbstractFactory {
private Phone phone = null;
@Override
public Phone create(String phoneType) {
switch (phoneType){
case "xiaomi":
phone = new XiaoMi();
phone.generatePhone();
break;
case "huawei":
phone = new HuaWei();
phone.generatePhone();
break;
default:
break;
}
return phone;
}
}
FactoryProducer:
在使用工厂的时候,只需要在其构造方法中传入特定的工厂类型、产品名称即可。在扩展工厂时,只需要传入新的工厂即可,不需要修改使用者代码,符合OCP原则。
/**
* 工厂选择
*/
public class FactoryProducer {
// 工厂
private AbstractFactory factory;
// 构造器
public FactoryProducer(AbstractFactory factory, String shopName){
setFactory(factory, shopName);
}
// 设置工厂类型并使用之
public void setFactory(AbstractFactory factory, String shopName){
this.factory = factory;
factory.create(shopName);
}
}
order客户端:
public class Order {
public static void main(String[] args) {
new FactoryProducer(new ComputerFactory(),"lianxiang");
new FactoryProducer(new ComputerFactory(),"huasuo");
new FactoryProducer(new PhoneFactory(),"xiaomi");
new FactoryProducer(new PhoneFactory(),"huawei");
/** output:
* LianXiang computer is generated
* HuaSuo computer is generated
* XIAOMI phone is generated
* HUaWei phone is generated
*/
}
}
使用抽象工厂类可以在扩展功能的时候只需要添加相应的工厂生产相应的对象即可,对于使用者而言不需要修改对买,直接使用即可。
在JDK的Calendar中使用到。
原型模式
**原型模式(prototype Pattern)**是通过实现一个原型接口,该接口用于创建当前对象的克隆。所以在需要创建重复的对象的时候可以使用该模式以保证性能。即:利用一个原型创建与之一样的对象。
缺点:对于新建的类问题不大,但是对于已有的类如果要实现原型模式需要修改原有的代码为其添加clone()方法。
使用:1.实现Cloneable接口;2.重写clone()方法;3.使用clone()方法获得复制的对象
如下:
- 需求:需要获得多个重复的Dog对象。
- Dog为实体类,实现了Cloneable接口;
- CloneDog类调用clone()方法,返回clone对象
- Clinet获取对象
Dog:
public class Dog implements Cloneable{
private String name;
private int age;
private double weight;
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
public void setWeight(double weight) {
this.weight = weight;
}
public Dog(String name, int age, double weight) {
this.name = name;
this.age = age;
this.weight = weight;
}
@Override
public String toString() {
return "Dog{" +
"name='" + name + '\'' +
", age=" + age +
", weight=" + weight +
'}';
}
// 重写lone方法
@Override
protected Object clone() {
Dog dog = null;
try {
dog = (Dog) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return dog;
}
}
CloneDog:
public class CloneDog {
public static Dog getDog(Dog dog){
return (Dog)dog.clone();
}
}
Client:
/**
* 获取对象
*/
public class Client {
public static void main(String[] args) {
Dog dog1 = new Dog("dog1", 1, 10.0);
Dog dog2 = CloneDog.getDog(dog1);
Dog dog3 = CloneDog.getDog(dog1);
Dog dog4 = CloneDog.getDog(dog1);
}
}
在Spring框架中的bean的scope中的prototype属性就是使用到了原型。
浅拷贝
- 浅拷贝对于基本数据类型(和String)的成员变量直接复制;
- 对于引用数据类型的成员变量使用引用传递,即多个拷贝的对象的引用成员变量"==";
- 浅拷贝默认使用clone()方法实现;
如下:
public class Dog implements Cloneable{
private String name;
private int age;
private double weight;
// 引用成员变量
private Dog parent;
// getter and setter
}
public static void main(String[] args) {
// 创建一个对象
Dog dog1 = new Dog("dog1", 1, 10.0);
// 设置对象的引用成员变量
dog1.setParent(new Dog("parent", 3, 20));
// 克隆
Dog dog2 = CloneDog.getDog(dog1);
// 两个对象本身不相等
System.out.println(dog1 == dog2); // false
// 但其引用成员变量相等,即浅拷贝
System.out.println(dog1.getParent() == dog2.getParent()); // true
}
深拷贝
- 复制对象的所有基本数据类型(和String);
- 复制对象的所有引用数据类型(重新申请空间),并且复制每个引用成员变量所引用的对象(即所有可达的对象),可以理解为顺藤摸瓜,全部拷贝。
实现方式:
-
重写clone()方法:
/** * Dog */ public class Dog implements Cloneable{ private String name; private int age; public Dog(String name, int age) { this.name = name; this.age = age; } @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); } }
/** * Cat */ public class Cat implements Cloneable{ private String name; private Dog friend; public Cat(String name, Dog friend) { this.name = name; this.friend = friend; } public Dog getFriend() { return friend; } // 重写clone方法 @Override protected Object clone() { Cat cat = null; try { // 拷贝自身 cat = (Cat)super.clone(); // 拷贝引用类型成员变量 cat.friend = (Dog) friend.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); } return cat; } }
public static void main(String[] args) { Cat cat = new Cat("cat", new Dog("dog", 1)); Cat cloneCat = (Cat)cat.clone(); System.out.println(cat == cloneCat); // false // 实现了深拷贝,其引用成员变量也被拷贝 System.out.println(cat.getFriend() == cloneCat.getFriend()); // false }
注意:
使用重写方式实现深拷贝,需要对每一个引用类型进行单独拷贝;所以所引用的每一个类都需要实现Cloneable接口。比较繁杂。 -
通过序列化来拷贝:
所有引用类都要实现Serializable接口以便序列化;在Cat类中添加方法:
// 序列化拷贝 public Object deepClone(){ ByteArrayOutputStream bos = null; ObjectOutputStream oos = null; ByteArrayInputStream bis = null; ObjectInputStream ois = null; try{ // 将对象序列化到内存中 bos = new ByteArrayOutputStream(); oos = new ObjectOutputStream(bos); oos.writeObject(this); // 从内存中反序列化对象 bis = new ByteArrayInputStream(bos.toByteArray()); ois = new ObjectInputStream(bis); Cat cat = (Cat) ois.readObject(); // 返回对象 return cat; }catch (Exception e){ e.printStackTrace(); return null; }finally { try { bos.close(); oos.close(); bis.close(); ois.close(); } catch (IOException e) { e.printStackTrace(); } } }
public static void main(String[] args) { Cat cat = new Cat("cat", new Dog("dog", 1)); // 序列化深拷贝 Cat cloneCat = (Cat)cat.deepClone(); System.out.println(cat == cloneCat); // false System.out.println(cat.getFriend() == cloneCat.getFriend()); // false }
推荐使用序列化方式实现深拷贝。
建造者模式
**建造者模式(Builder Pattern)**使用Builder类来一步一步构建一个复杂的对象。将对象的构建过程与其本身分离以实现解耦。使用构造类可以便于扩展,当有新的产品的时候,增加建造者类即可。所以也会导致建造者类过多。适用于产品的差异度不大的场景。
简单场景理解:车的零件会各有不同,零件会经常发生变化,但是车的建造过程是不变的,即类的内部属性在不断变化,但其构成过程是相对稳定的,所以可以使用建造者模式。
与工厂模式的区别:工厂模式模式注重产品的创建;建造者模式还需要注重产品的生产过程,突出的是过程。
如下:
- 需求:需要建造交通工具:car和bus。
- Vehicle类表示交通工具,其内部有属性,car和bus属性不同;
- AbsVehicleBuilder为抽象建造者类,框定的Vehicle的总体构建行为(方法);
- BusBuilder、CarBuilder为具体的建造者,建造者中的方法为实际的构建过程;
- Director类为指挥者,作用为控制Vehicle的建造类型和建造步骤,通过传入的建造者对象决定建造什么类型的对象;
- Client类为客户端,即用户需求。
Vehicle实体类(产品):
/**
* 交通工具实体(被建造者)
*/
public class Vehicle {
private String type;
private String chassis; // 底盘
private String engine; // 发动机
private String wheels; // 轮子
private String shell; // 外壳
// setter and toString
AbsVehicleBuilder抽象建造者:
/**
* 建造者抽象类
*/
public abstract class AbsVehicleBuilder {
public Vehicle vehicle = new Vehicle();
// 组装底盘
public abstract void buildChassis(String chassis);
// 组装发动机
public abstract void buildEngine(String engine);
// 组装轮子
public abstract void buildWheels(String wheels);
// 组装外壳
public abstract void buildShell(String shell);
// 建造完成输出车辆
public Vehicle completeBuild(){
return vehicle;
}
}
Bus建造者:
/**
* bus建造者
*/
public class BusBuilder extends AbsVehicleBuilder{
public BusBuilder() {
vehicle.setType("Bus");
}
// 组装底盘
public void buildChassis(String chassis){
vehicle.setChassis(chassis);
}
// 组装发动机
public void buildEngine(String engine){
vehicle.setEngine(engine);
}
// 组装轮子
public void buildWheels(String wheels){
vehicle.setWheels(wheels);
}
// 组装外壳
public void buildShell(String shell){
vehicle.setShell(shell);
}
}
Car建造者:
/**
* car建造者
*/
public class CarBuilder extends AbsVehicleBuilder{
public CarBuilder() {
vehicle.setType("Car");
}
// 组装底盘
public void buildChassis(String chassis){
vehicle.setChassis(chassis);
}
// 组装发动机
public void buildEngine(String engine){
vehicle.setEngine(engine);
}
// 组装轮子
public void buildWheels(String wheels){
vehicle.setWheels(wheels);
}
// 组装外壳
public void buildShell(String shell){
vehicle.setShell(shell);
}
}
Director指挥者:
/**
* 指挥者,用于协调建造;
* 确定建造的类型和建造的顺序
*/
public class Director {
// 具体的建造者
AbsVehicleBuilder vehicleBuilder = null;
// 通过构造方法传入某类型交通工具建造者
public Director(AbsVehicleBuilder vehicleBuilder) {
this.vehicleBuilder = vehicleBuilder;
}
// 建造交通工具并返回一个构造完成的对象
public Vehicle buildVehicle(String chassis, String engine, String wheels, String shell){
vehicleBuilder.buildChassis(chassis);
vehicleBuilder.buildEngine(engine);
vehicleBuilder.buildWheels(wheels);
vehicleBuilder.buildShell(shell);
return vehicleBuilder.vehicle;
}
}
客户端:
public static void main(String[] args) {
// 建造car
Director director = new Director(new CarBuilder());
Vehicle car = director.buildVehicle("x底盘", "xx发动机", "xx轮子", "xx外壳");
// Vehicle{type='Car', chassis='x底盘', engine='xx发动机', wheels='xx轮子', shell='xx外壳'}
// 建造bus
Director busDirector = new Director(new BusBuilder());
Vehicle bus = busDirector.buildVehicle("bus底盘", "bus发动机", "bus轮子", "bus外壳");
// Vehicle{type='Bus', chassis='bus底盘', engine='bus发动机', wheels='bus轮子', shell='bus外壳'}
}
JDK中的使用
StringBuilder:
由上UML图可知:
- Appendable为抽象建造者,其中定义String的抽象构建方法;
- AbstractStringBuilder类是一个具体的实现建造者,但是是抽象类(内部很多方法都已经具体实现,但是不能实现该类);
- StringBuilder为具体的建造者,使用该建造者类就可以建造String。