设计模式的七个原则:
每个类A都避免不了与其他类B产生关系,但我们让这种关系越小越好。即类A对类B的内部了解的越少越好,如果类A要用到类B,最好只需要调用类B提供的public方法即可。不允许把另一个类的对象当做自己的局部变量。
设计模式的七个原则详解
案例
学习及实例博客
反序列化破坏单例解决方法 :
重写readResolve()方法即可
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
private Singleton (){}
public static final Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
private Object readResolve() {
return singleton;
}
容器式单例
当程序中的单例对象非常多的时候,则可以使用容器对所有单例对象进行管理,如下:
单独提出一个容器保存单例
public class ContainerSingleton {
private ContainerSingleton() {}
private static Map<String, Object> singletonMap = new ConcurrentHashMap<>();
public static Object getInstance(Class clazz) throws Exception {
String className = clazz.getName();
// 当容器中不存在目标对象时则先生成对象再返回该对象
if (!singletonMap.containsKey(className)) {
Object instance = Class.forName(className).newInstance();
singletonMap.put(className, instance);
return instance;
}
// 否则就直接返回容器里的对象
return singletonMap.get(className);
}
public static void main(String[] args) throws Exception {
SafetyDangerLibrary instance1 = (SafetyDangerLibrary)ContainerSingleton.getInstance(SafetyDangerLibrary.class);
SafetyDangerLibrary instance2 = (SafetyDangerLibrary)ContainerSingleton.getInstance(SafetyDangerLibrary.class);
System.out.println(instance1 == instance2); // true
}
}
ThreadLocal单例
不保证整个应用全局唯一,但保证线程内部全局唯一,以空间换时间,且线程安全。
public class ThreadLocalSingleton {
private ThreadLocalSingleton(){}
private static final ThreadLocal<ThreadLocalSingleton> threadLocalInstance = ThreadLocal.withInitial(() -> new ThreadLocalSingleton());
public static ThreadLocalSingleton getInstance(){
return threadLocalInstance.get();
}
public static void main(String[] args) {
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + "-----" + ThreadLocalSingleton.getInstance());
System.out.println(Thread.currentThread().getName() + "-----" + ThreadLocalSingleton.getInstance());
}).start();
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + "-----" + ThreadLocalSingleton.getInstance());
System.out.println(Thread.currentThread().getName() + "-----" + ThreadLocalSingleton.getInstance());
}).start();
// Thread-0-----com.ruoyi.library.domain.vo.ThreadLocalSingleton@53ac93b3
// Thread-1-----com.ruoyi.library.domain.vo.ThreadLocalSingleton@7fe11afc
// Thread-0-----com.ruoyi.library.domain.vo.ThreadLocalSingleton@53ac93b3
// Thread-1-----com.ruoyi.library.domain.vo.ThreadLocalSingleton@7fe11afc
}
}
通过克隆实现
克隆破坏单例解决方法 :
重写clone()方法即可:
private static Clazz clazz = new Clazz();
// 方法一
@Override
protected Object clone() throws CloneNotSupportedException {
// 返回类中原有的实例即可
return clazz;
}
// 测试输出
System.out.println(clazz1 == clazz2) // true
新建类时提供一系列方法去设置新建类的参数,不需要开发者一个个单独调用设置。
装饰模式,是指在不改变原有对象的基础上,将功能附加到对象上,提供了比继承更有弹性的替代方案(扩展原有对象的功能)
装饰(Decorator)模式中的角色:
装饰器实例 :
public class Test {
// 测试方法
public static void main(String[] args) {
FriedRice friedRice = new FriedRice();
System.out.println(friedRice.getDesc() + friedRice.getPrice() + "元"); // 炒饭5元
friedRice = new Egg(friedRice);
System.out.println(friedRice.getDesc() + friedRice.getPrice() + "元"); // 炒饭+鸡蛋7元
friedRice = new Egg(friedRice);
System.out.println(friedRice.getDesc() + friedRice.getPrice() + "元");// 炒饭+鸡蛋+鸡蛋9元
friedRice = new Ham(friedRice);
System.out.println(friedRice.getDesc() + friedRice.getPrice() + "元");// 炒饭+鸡蛋+鸡蛋+火腿12元
}
}
// 炒饭类
class FriedRice {
String getDesc() {
return "炒饭";
}
Integer getPrice() {
return 5;
}
}
// 配料表
abstract class Ingredients extends FriedRice {
private FriedRice friedRice;
public Ingredients(FriedRice friedRice) {
this.friedRice = friedRice;
}
String getDesc() {
return this.friedRice.getDesc();
}
Integer getPrice() {
return this.friedRice.getPrice();
}
}
// 鸡蛋配料
class Egg extends Ingredients {
public Egg(FriedRice friedRice) {
super(friedRice);
}
String getDesc() {
return super.getDesc() + "+鸡蛋";
}
Integer getPrice() {
return super.getPrice() + 2;
}
}
// 火腿配料
class Ham extends Ingredients {
public Ham(FriedRice friedRice){
super(friedRice);
}
String getDesc() {
return super.getDesc() + "+火腿";
}
Integer getPrice() {
return super.getPrice() + 3;
}
}
创建装饰器抽象类实现待装饰接口类,并通过构造参数传入组合保存一个待装饰的接口类,子类继承创建装饰器抽象类并实现需要的方法
享元模式又称为轻量级模式,是对象池的一种实现,类似于线程池,线程池可以避免不停的创建和销毁多个对象,消耗性能。提供了减少对象数量从而改善应用所需的对象结构的方式。
宗旨:共享细粒度对象,将多个对同一对象的访问集中起来
。
案例 :
// 抽象接口
public interface ITicket {
void show(String seat);
}
public class TrainTicket implements ITicket {
private String from;
private String to;
private Integer price;
public TrainTicket(String from, String to) {
this.from = from;
this.to = to;
}
@Override
public void show(String seat) {
this.price = new Random().nextInt(500);
System.out.println(from + "->" + to + ":" + seat + "价格:" + this.price);
}
}
// 工厂类
public class TicketFactory {
private static Map<String, ITicket> pool = new ConcurrentHashMap<>();
public static ITicket getTicket(String from, String to) {
String key = from + "->" + to;
if (pool.containsKey(key)) {
System.out.println("使用缓存获取火车票:" + key);
return pool.get(key);
}
System.out.println("使用数据库获取火车票:" + key);
ITicket ticket = new TrainTicket(from, to);
pool.put(key, ticket);
return ticket;
}
}
// 测试
public static void main(String[] args) {
ITicket ticket = getTicket("北京", "上海");
//使用数据库获取火车票:北京->上海
//北京->上海:二等座价格:20
ticket.show("二等座");
ITicket ticket1 = getTicket("北京", "上海");
//使用缓存获取火车票:北京->上海
//北京->上海:商务座价格:69
ticket1.show("商务座");
ITicket ticket2 = getTicket("上海", "北京");
//使用数据库获取火车票:上海->北京
//上海->北京:一等座价格:406
ticket2.show("一等座");
System.out.println(ticket == ticket1);//true
System.out.println(ticket == ticket2);//false
}
定义一个算法的骨架,并允许之类为其中的一个或者多个步骤提供实现。模板方法模式使得子类可以在不改变算法结构的情况下,重新定义算法的某些步骤,
将可变的行为留给子类来实现,各子类中公共的行为被提取出来并集中到一个公共的父类中,从而避免代码重复。
首先定义一个抽象类,在抽象类中先定义好执行的总体方法,在总体方法内细化每个执行步骤,每个步骤就是对应抽象类中的一个抽象方法。子类通过继承抽象类重写需要的抽象方法,再调用总体执行方法即可
public abstract class DayOffProcess {
// 请假模板
public final void dayOffProcess() {
// 领取申请表
this.pickUpForm();
// 填写申请信息
this.writeInfo();
// 签名
this.signUp();
// 提交到不同部门审批
this.summit();
// 行政部备案
this.filing();
}
private void filing() {
System.out.println("行政部备案");
}
protected abstract void summit();
protected abstract void signUp();
private void writeInfo() {
System.out.println("填写申请信息");
}
private void pickUpForm() {
System.out.println("领取申请表");
}
}
上例可通过不同类实现不同方法,达到不同目的
public class ZhangSan extends DayOffProcess {
@Override
protected void summit() {
System.out.println("张三签名");
}
@Override
protected void signUp() {
System.out.println("提交到技术部审批");
}
}
public class Lisi extends DayOffProcess {
@Override
protected void summit() {
System.out.println("李四签名");
}
@Override
protected void signUp() {
System.out.println("提交到市场部审批");
}
通过一个Map,实现不同入参返回不同的类型,可以避免多重分支的if…else和switch语句。
// 会员卡接口
public interface VipCard {
public void discount();
}
public class GoldCard implements VipCard {
@Override
public void discount() {
System.out.println("金卡打7折");
}
}
public class SilverCard implements VipCard {
@Override
public void discount() {
System.out.println("银卡打8折");
}
}
public class CopperCard implements VipCard {
@Override
public void discount() {
System.out.println("铜卡打9折");
}
}
public class Normal implements VipCard {
@Override
public void discount() {
System.out.println("普通会员没有折扣");
}
}
// 会员卡容器类
public class VipCardFactory {
private static Map<String, VipCard> map = new ConcurrentHashMap<>();
static {
map.put("gold", new GoldCard());
map.put("silver", new SilverCard());
map.put("copper", new CopperCard());
}
public static VipCard getVIPCard(String level) {
return map.get(level) != null ? map.get(level) : new Normal();
}
}
// 测试方法
public static void main(String[] args) {
//金卡打7折
VipCardFactory.getVIPCard("gold").discount();
//银卡打8折
VipCardFactory.getVIPCard("silver").discount();
//普通会员没有折扣
VipCardFactory.getVIPCard("other").discount();
}
命令模式包含以下主要角色:
调用者只负责调用实现者的实现接口,不管实现者内部逻辑,实现者组合一个具体命令类,实现者根据不同需求在实现者的实现接口内调用不同的具体命令类的某个方法。
// 播放器类
public class Player {
public void play() {
System.out.println("正常播放");
}
public void pause() {
System.out.println("暂停播放");
}
public void stop() {
System.out.println("停止播放");
}
}
// 命令接口
public interface IAction {
void excuse();
}
// 播放命令类
@AllArgsConstructor
public class PlayAction implements IAction {
private Player player;
@Override
public void excuse() {
this.player.play();
}
}
// 暂停命令类
@AllArgsConstructor
public class PauseAction implements IAction {
private Player player;
@Override
public void excuse() {
this.player.pause();
}
}
// 停止命令类
@AllArgsConstructor
public class StopAction implements IAction{
private Player player;
@Override
public void excuse() {
this.player.stop();
}
}
// 控制器
public class Controller {
public void excuse(IAction action) {
action.excuse();
}
}
// 测试方法
public static void main(String[] args) {
// 正常播放
new Controller().excuse(new PlayAction(new Player()));
// 暂停播放
new Controller().excuse(new PauseAction(new Player()));
// 停止播放
new Controller().excuse(new StopAction(new Player()));
}
职责链模式是将链中每一个节点看作是一个对象,每个节点处理的请求均不同,且内部自动维护一个下一节点对象。当一个请求从链式的首端发出时,会沿着链的路径依次传递给每一个节点对象,直至有对象处理这个请求为止。
在一条已经定义好的责任链上,只能往链子开头新增,无法对链子结尾新增,因为在链结尾已经定义好责任链的结束并返回逻辑,不会再往后执行。
// 用户实体类
@Data
public class User {
private String username;
private String password;
private String role;
}
// handler抽象类
public abstract class Handler {
protected Handler next;
// 返回handler方便链式操作
public void next(Handler next) {
this.next = next;
}
// 流程开始的方法
public abstract void doHandler(User user);
}
// 校验用户名或者密码是否为空
public class ValidateHandler extends Handler {
@Override
public void doHandler(User user) {
if (StringUtils.isBlank(user.getUsername()) || StringUtils.isBlank(user.getPassword())) {
System.out.println("用户名或者密码为空!");
return;
}
System.out.println("校验通过");
next.doHandler(user);
}
}
// 登录校验,校验用户名是否匹配密码
public class LoginHandler extends Handler {
@Override
public void doHandler(User user) {
if (!"pyy52hz".equals(user.getUsername()) || !"123456".equals(user.getPassword())) {
System.out.println("用户名或者密码不正确!请检查!");
return;
}
user.setRole("admin");
System.out.println("登陆成功!角色为管理员!");
next.doHandler(user);
}
}
// 权限校验
public class AuthHandler extends Handler {
@Override
public void doHandler(User user) {
if (!"admin".equals(user.getRole())) {
System.out.println("无权限操作!");
return;
}
System.out.println("角色为管理员,可以进行下一步操作!");
}
}
// 登录流程
public class LoginService {
public void login(User user) {
Handler validateHandler = new ValidateHandler();
Handler loginHandler = new LoginHandler();
Handler authHandler = new AuthHandler();
validateHandler.next(loginHandler);
loginHandler.next(authHandler);
validateHandler.doHandler(user);
}
}
// 测试方法
public static void main(String[] args){
User user = new User();
//校验通过
//用户名或者密码不正确!请检查!
user.setUsername("pyy52hz");
user.setPassword("1234567");
LoginService loginService = new LoginService();
loginService.login(user);
//校验通过
//登陆成功!角色为管理员!
//角色为管理员,可以进行下一步操作!
user.setUsername("pyy52hz");
user.setPassword("123456");
loginService.login(user);
}
结合建造者模式:
// handler抽象类
public abstract class Handler<T> {
protected Handler next;
// 返回handler方便链式操作
public Handler next(Handler next) {
this.next = next;
return next;
}
// 流程开始的方法
public abstract void doHandler(User user);
static class Builder<T> {
private Handler<T> head;
private Handler<T> tail;
public Builder<T> addHandler(Handler<T> handler) {
if (this.head == null) {
this.head = this.tail = handler;
return this;
}
this.tail.next(handler);
this.tail = handler;
return this;
}
public Handler<T> build() {
return this.head;
}
}
}
public class LoginService {
public void login(User user) {
Handler.Builder builder = new Handler.Builder();
builder.addHandler(new ValidateHandler())
.addHandler(new LoginHandler())
.addHandler(new AuthHandler());
builder.build().doHandler(user);
}
}
略,就是通过调用不同方法改变改变类中依赖类的属性,依赖类定义在顶级的抽象类中。
观察者模式,又叫发布-订阅(Publish/Subscribe)模式,模型-视图(Model/View)模式,源-监听器(Source/Listener)模式或从属者(Dependents)模式。定义一种一对多的依赖关系,一个主题对象可被多个观察者同时监听,使得每当主题对象状态变化时,所有依赖于它的对象都会得到通知并被自动更新。
定义被观察者接口,通过组合方式里面保存着一个全是观察者的集合,被观察者提供一个方法循环调用集合中观察者的观察接口
// 抽象观察者接口
public interface Observer {
void update(String message);
}
// 微信用户类 具体的观察者
@AllArgsConstructor
public class WeixinUser implements Observer {
private String name;
@Override
public void update(String message) {
System.out.println(name + "接收到了消息(观察到了):" + message);
}
}
// 被观察者接口
public interface Observable {
// 新增用户(新增观察者)
void add(Observer observer);
// 移除用户,或者说用户取消订阅(移除观察者)
void del(Observer observer);
// 发布 推送消息
void notify(String message);
}
// 具体的被观察者(公众号)
public class Subject implements Observable {
// 观察者列表(订阅用户)
private List<Observer> list = new ArrayList<>();
@Override
public void add(Observer observer) {
list.add(observer);
}
@Override
public void del(Observer observer) {
list.remove(observer);
}
// 给每一个观察者(订阅者)推送消息
@Override
public void notify(String message) {
list.forEach(observer -> observer.update(message));
}
}
// 测试
public static void main(String[] args){
Observable o = new Subject();
WeixinUser user1 = new WeixinUser("张三");
WeixinUser user2 = new WeixinUser("李四");
WeixinUser user3 = new WeixinUser("王五");
o.add(user1);
o.add(user2);
o.add(user3);
o.notify("薛之谦演唱会要来到广州啦!");
// 运行结果
// 张三接收到了消息(观察到了):薛之谦演唱会要来到广州啦!
// 李四接收到了消息(观察到了):薛之谦演唱会要来到广州啦!
// 王五接收到了消息(观察到了):薛之谦演唱会要来到广州啦!
}
定义一个被关联接口,有多个实现类称为具体类,再定义一个中介类,将具体类都组合在中介类中,中介类暴露一个方法,其返回值为组合内某个具体类的属性,通过入参不同来确定的具体类。
// 抽象同事类
@AllArgsConstructor
public class Person {
protected String name;
protected MediatorCompany mediatorCompany;
}
// 房主
public class HouseOwner extends Person {
public HouseOwner(String name, MediatorCompany mediatorCompany) {
super(name, mediatorCompany);
}
// 联络方法
public void connection(String message) {
mediatorCompany.connection(this, message);
}
// 获取消息
public void getMessage(String message) {
System.out.println("房主" + name + "获取到的信息:" + message);
}
}
// 租客
public class Tenant extends Person {
public Tenant(String name, MediatorCompany mediatorCompany) {
super(name, mediatorCompany);
}
public void connection(String message) {
mediatorCompany.connection(this, message);
}
public void getMessage(String message) {
System.out.println("租客" + name + "获取到的信息:" + message);
}
}
// 中介公司(中介者)
@Data
public class MediatorCompany {
// 组合
private HouseOwner houseOwner;
private Tenant tenant;
public void connection(Person person, String message) {
// 房主需要通过中介获取租客信息
if (person.equals(houseOwner)) {
this.tenant.getMessage(message);
} else { // 反之租客通过中介获取房主信息
this.houseOwner.getMessage(message);
}
}
}
// 测试
public static void main(String[] args){
// 先创建三个角色,中介公司,房主,租客
MediatorCompany mediatorCompany = new MediatorCompany();
// 房主和租客都在同一家中介公司
HouseOwner houseOwner = new HouseOwner("张三", mediatorCompany);
Tenant tenant = new Tenant("李四", mediatorCompany);
// 中介公司获取房主和租客的信息
mediatorCompany.setHouseOwner(houseOwner);
mediatorCompany.setTenant(tenant);
// 房主和租客都在这家中介公司发布消息,获取到对应的消息
tenant.connection(tenant.name + "想租一房一厅!");
houseOwner.connection(houseOwner.name + "这里有!来看看呗!");
// 测试结果
// 房主张三获取到的信息:李四想租一房一厅!
// 租客李四获取到的信息:张三这里有!来看看呗!
}
定义一个迭代器接口,包含一个集合元素,一个当前遍历的游标。
// 迭代器接口
public interface Iterator<T> {
Boolean hasNext();
T next();
}
// 迭代器接口实现类
public class IteratorImpl<T> implements Iterator<T> {
private List<T> list;
private Integer cursor;
private T element;
public IteratorImpl(List<T> list) {
this.list = list;
}
@Override
public Boolean hasNext() {
return cursor < list.size();
}
@Override
public T next() {
element = list.get(cursor);
cursor++;
return element;
}
}
// 容器接口
public interface Aggregate<T> {
void add(T t);
void remove(T t);
Iterator<T> iterator();
}
// 容器接口实现类
public class AggregateImpl<T> implements Aggregate<T> {
private List<T> list = new ArrayList<>();
@Override
public void add(T t) {
list.add(t);
}
@Override
public void remove(T t) {
list.remove(t);
}
@Override
public Iterator<T> iterator() {
return new IteratorImpl<>(list);
}
}
定义一个主类,一个备份类,备份类保存着主类需要备份的属性。主类中必须包含两个方法,一个存档方法,一个恢复方法。存档方法返回一个备份类,并将主类需要备份的属性赋值给备份类;恢复方法输入一个备份类,负责将备份类的属性赋值给当前主类。
// 游戏角色类
@Data
public class GameRole {
private Integer vit; // 生命力
private Integer atk; // 攻击力
private Integer def; // 防御力
// 初始化状态
public void init() {
this.vit = 100;
this.atk = 100;
this.def = 100;
}
// 战斗到0
public void fight() {
this.vit = 0;
this.atk = 0;
this.def = 0;
}
// 保存角色状态
public RoleStateMemento saveState() {
return new RoleStateMemento(this.vit, this.atk, this.def);
}
// 回复角色状态
public void recoverState(RoleStateMemento roleStateMemento) {
this.vit = roleStateMemento.getVit();
this.atk = roleStateMemento.getAtk();
this.def = roleStateMemento.getDef();
}
// 展示状态
public void showState() {
System.out.println("角色生命力:" + this.vit);
System.out.println("角色攻击力:" + this.atk);
System.out.println("角色防御力:" + this.def);
}
}
// 游戏状态存储类(备忘录类)
@Data
@AllArgsConstructor
public class RoleStateMemento {
private Integer vit; // 生命力
private Integer atk; // 攻击力
private Integer def; // 防御力
}
// 角色状态管理者类
@Data
public class RoleStateCaretaker {
private RoleStateMemento roleStateMemento;
}
// 测试结果
public static void main(String[] args){
System.out.println("===========打boss前状态===========");
GameRole gameRole = new GameRole();
gameRole.init();
gameRole.showState();
// 保存进度
RoleStateCaretaker roleStateCaretaker = new RoleStateCaretaker();
roleStateCaretaker.setRoleStateMemento(gameRole.saveState());
System.out.println("===========打boss后状态===========");
gameRole.fight();
gameRole.showState();
System.out.println("===========恢复状态===========");
gameRole.recoverState(roleStateCaretaker.getRoleStateMemento());
gameRole.showState();
// ===========打boss前状态===========
// 角色生命力:100
// 角色攻击力:100
// 角色防御力:100
// ===========打boss后状态===========
// 角色生命力:0
// 角色攻击力:0
// 角色防御力:0
// ===========恢复状态===========
// 角色生命力:100
// 角色攻击力:100
// 角色防御力:100
}