常用的设计模式
设计模式看似把代码改造了很多,其实,只不过是把代码挪动了一下位置,增加了一些小小的变量,删减了一些小小的变量。历史
设计模式一开始是由一个搞建筑的人提出的。GoF,Gang of Four,四人组。指的是一本书,四个人写的,因为名字太长了,就叫做这个。原书名好像是叫做:《设计模式:面向对象软件设计的基础》
这本书中提出的设计模式一共有23种。
设计原则
设计原则按照字母手写简写可以概括为`SOLID`原则。单一职责原则(Single Responsibility Principle)
开放封闭原则(Open Close Principle)
里氏替换原则(Liskov Substitution Principle)
迪米特法则(Least Knowledge Principle)
接口分离原则(Interface Segregation Principle)
依赖倒置原则(Dependency Inversion Principle)
单一职责
一个模块负责一个功能。比如 controller 层有多个 controller,service 层有多个 service,每个模块负责的功能都不一样。
如果都混在一起,那么:
[*]寻找代码很困难
[*]修改代码很麻烦
开闭原则
对修改关闭,对扩展开放。如果要扩展功能,尽量不要修改原有代码,而是实现接口或者继承父类。
里氏替换原则
S 是 F 的子类,如果 S 的编译类型是 F,那么 S 的行为尽量和 F 保持一致。意思就是子类尽量不要覆盖父类的方法。
override:推翻、覆盖。
之所以被翻译成“重写”,我认为是 override 看起来或者听起来有点像 overwrite,而它确实有“覆盖,重写”的意思,但并不是关键字,而且“重写”听起来不如“覆盖”那么那么容易理解。
迪米特法则
也叫最少知道原则。一个类尽量与自已有直接依赖关系的类或接口产生联系。
比如 controller 中只与 service 层产生直接联系,没有 dao 层的依赖;
明星与经纪人是直接联系,经纪人和粉丝直接联系。
接口隔离原则
不要试图创建一个很庞大的接口,而应该针对特定的功能去设计接口。比如 person 接口有 eat,study,work,sleep 四个方法,那么 Teacher 类和 Student 类实现这个接口的时候,都必须实现这四个方法,但是 Teacher 无需实现 study 方法,Student 类也无需实现 work 方法,这就造成代码冗余了。
更好的方法应该是,创建三个接口:Person,Teacher,Student,Person 只有 eat,sleep 方法,Student 接口只有 study 方法,Teacher 类只有 work 方法。
那么,老师类实现 Person、Teacher 这两个接口,学生类实现 Person、Student 这两个接口。
依赖倒置原则
高层模块尽量依赖抽象,而不是具体的实现类。比如 controller 类应该依赖的是 Service 接口,而不是 Service 实现类。
https://cdn.nlark.com/yuque/0/2024/jpeg/38672378/1732894596042-622fec80-3a4c-4ff3-97c9-ac7acaa56c49.jpeg
有什么好处呢?
如果要替换 Service 实现类,那么就只需要替换实现类,而Service,controller 是不用变的。
创建型
单例模式
饿汉式
1. 构造器私有化2. 创建静态对象3. 提供外部统一访问方法package singleton.case1;
/**
* 单例模式:饿汉式
*/
public class Singleton1 {
/**
* 必须是静态的,如果不是静态的,那么 getInstance 方法就无法使用这个变量名
* 静态方法只能使用静态成员。
* 因为非静态成员是属于对象的,但是调用非静态成员必须创建对象。
*/
private static Singleton1 instance = new Singleton1();
private Singleton1() {
}
public static Singleton1 getInstance() {
return instance;
}
}为什么 getInstance 方法是静态的?
因为如果是非静态的,那么必须创建对象,来调用这个方法,但是构造器是不能被外部调用的。
为什么 instance 是静态的?
如果不是静态的,那么 getInstance 方法就无法使用这个变量名。
为什么静态方法只能使用静态成员?
静态方法不属于对象,属于类,因此调用静态方法的时候,是类在调用。方法体中,返回一个对象,返回的是方法调用者的对象,也就是类对象,所以 instance 是静态的。
如果是非静态方法,那么就是对象在调用,方法体中,返回的对象,是属于对象的。对象的成员可以是静态的,也可以是非静态的。
非静态方法返回的对象,默认是属于 this 的,this 表示当前方法的调用者(实例对象)。这也就是为什么静态方法不能出现 this 的原因。
总结:
方法体中出现 this,this 表示当前方法的调用者,调用者是实例对象;
静态方法的调用者是类本身,因此静态方法中不可能出现 this。也不可能出现非静态成员,因为非静态成员是和实例对象相关联的。
工厂模式
一个方法,一个参数,就能创建指定的产品看到XXXFactory形式的类,就要想到这是一个工厂类,它使用了工厂模式,例如 Spring 的BeanFactory、Mybatis 的 SqlSessionFactory。
简单工厂模式
工厂直接创建产品package com.cskaoyan.fatory.simple;
import com.cskaoyan.fatory.bean.Animal;
import com.cskaoyan.fatory.bean.Pig;
import com.cskaoyan.fatory.bean.Rabbit;
/**
* 简单工厂
*/
public class SimpleAnimalFactory {
/**
* 提供一个方法,根据不同的参数,获取不同的对象实例
* @param animalName
* @return
*/
public static Animal getAnimal(String animalName) {
if (animalName.equals("pig")) {
return new Pig();
}
if (animalName.equals("rabbit")) {
return new Rabbit();
}
return null;
}
}package com.cskaoyan.fatory.simple;
import com.cskaoyan.fatory.bean.Animal;
public class Case1 {
public static void main(String[] args) {
Animal pig = SimpleAnimalFactory.getAnimal("pig");
Animal rabbit = SimpleAnimalFactory.getAnimal("rabbit");
int m = 0;
}
}工厂方法模式
由子工厂来创建产品package com.cskaoyan.fatory.method;
import com.cskaoyan.fatory.bean.Animal;
/**
*
* 工厂方法设计模式:
*
* 通过不同的工厂实现类,就可以获取不同的对象实例
*
* 工厂方法中,只有一个方法,只能生产单个产品
* 而抽象工厂中,有多个方法,可以生产的是一个产品矩阵
*/
public interface AnimalFactory {
Animal getAnimal();
}package com.cskaoyan.fatory.method;
import com.cskaoyan.fatory.bean.Animal;
import com.cskaoyan.fatory.bean.Pig;
public class PigAnimalFactory implements AnimalFactory{
@Override
public Animal getAnimal() {
return new Pig();
}
}package com.cskaoyan.fatory.method;
import com.cskaoyan.fatory.bean.Animal;
import com.cskaoyan.fatory.bean.Rabbit;
public class RabbitAnimalFactory implements AnimalFactory{
@Override
public Animal getAnimal() {
return new Rabbit();
}
}抽象工厂模式
一个工厂有多个方法,可以生产一系列产品public abstract class AbstractFurnitureFactory {
public abstract TV createTV();
public abstract Freezer createFreezer();
}public class MiFurnitureFactory extends AbstractFurnitureFactory{
@Override
public TV createTV() {
return new MiTV();
}
@Override
public Freezer createFreezer() {
return new MiFreezer();
}
}public class HaierFurnitureFactory extends AbstractFurnitureFactory{
@Override
public TV createTV() {
return new HaierTV();
}
@Override
public Freezer createFreezer() {
return new HaierFreezer();
}
}public class OrderFurniture {
public static void main(String[] args) {
MiFurnitureFactory miFactory = new MiFurnitureFactory();
TV tv = miFactory.createTV();
Freezer freezer = miFactory.createFreezer();
System.out.println("tv instanceof MiTV = " + (tv instanceof MiTV));
System.out.println("freezer instanceof MiFreezer = " + (freezer instanceof MiFreezer));
}
}建造者模式
形式:`XXXBuilder`使用场景:创建复杂对象。一步一步形成一个完整的对象,每一步又可能涉及到其他对象。
现有的例子:StringBuilder、Minio、ES、@Builder 注解……
StringBuilder
package com.cskaoyan.builder.case1;
public class Case1 {
public static void main(String[] args) {
/**
* 建造者模式的代码
*/
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append("hello")
.append(" ")
.append("cskaoyan")
.append(" ")
.append("2025")
.append("!");
String content = stringBuilder.toString();
System.out.println(content);
}
}
// 对比set:这种方式更加连续,更加紧凑,set 面对复杂对象容易出错。@Builder注解
// 这个注解,其实就是在编译阶段,帮助我们生成这个对象的builder类
// 但是,需要注意的是,一旦某个类被添加了@Builder注解,那么就意味着这个类没有无参构造方法了
// 没有无参构造方法,
// <1> 那么就意味着不能直接new对象了
// <2> 那么也意味着,在和很多的框架整合的时候,可能会出现问题
// mybatis
// FastJSON
// RedissonClient
// 为什么这些框架会有问题呢?
// 比如使用RedissonClient存一个teacher对象到Redis中,那么此时Redis中存储的是Teacher对象的JSON字符串
// 但是在取这个对象的时候,首先RedissonClient会从Redis中查询到这个JSON字符串
// 然后需要把这个JSON字符串转化为Teacher对象
// 使用无参构造方法创建一个teacher对象
// 设值
// 因为现在增加了@Builder注解之后,没有无参构造方法,所以此时就会报错
// 记结论
// 使用@builder注解的时候,需要配合 @NoArgsConstructor注解 和 @AllArgsConstructor注解一起使用结构型
代理模式
行为型
责任链模式
上下文对象可以注册到容器中吗?不可以,因为上下文对象中有值,每个订单不同的订单id。
哪些对象可以复用?
对象里面的数据可以复用,这个对象就能注册到容器中。
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
页:
[1]