目录
享元模式
提供了减少对象数量从而改善应用所需的对象结构的方式。运用共享技术有效的支持大量细粒度的对象。简单说就是减少创建对象的数量,减少内存的占用,提高性能。
类型:结构型
使用场景:常用于系统底层开发,以解决系统的性能问题。系统有大量相似对象,需要缓冲池的场景。
优点:减少对象创建,降低内存中对象的数量,降低系统的内存,提高效率。减少内存之外其他资源的占用。
缺点:需要关注内部外部的状态,关注线程安全问题。使系统、程序的逻辑复杂化(使用享元对象还要分离出内部外部状态,外部状态不应随着内部状态变化而变化,否则系统就会分乱了。)
补充概念:
内部状态:在享元对象内部,不会随着环境改变而改变的部分。
外部状态:会随着环境改变而改变,是不可以共享的状态。
例子:
public interface Game {
void play();
}
public class Player implements Game {
private String username;
private String code;
private String role = "玩家";
public Player(String username) {
this.username = username;
}
public void setCode(String code) {
this.code = code;
}
@Override
public void play() {
System.out.println(role + username + "玩游戏");
}
}
public class PlayerManager {
private static final Map<String , Player> PLAYER_MAP = new HashMap<>();
public static Player getPlayer(String username){
Player player = PLAYER_MAP.get(username);
if(player == null){
System.out.println("创建账号:" + username);
player = new Player(username);
String code = "code" + PLAYER_MAP.size();
player.setCode(code);
PLAYER_MAP.put(username , player);
}
return player;
}
}
public class Test {
private static final String players[] = {"haozi","rou","mark","michael","clark"};
public static void main(String[] args) {
for (int i = 0 ; i < 10 ; i ++){
String username = players[(int)(Math.random() * players.length)];
Player player1 = PlayerManager.getPlayer(username);
player1.play();
}
}
}
类图
PlayerManager来创建和管理Player,Player实现Game接口,player中,role就属于内部状态,其他两个属于外部状态。
用到设计模式的源码:
Integer#valueOf
组合模式
将对象组成树形结构以表示“整体-部分”的层次结构。使客户端对单个对象和组合对象保持一致的处理方式。简单地说就是把多个对象组合成一个对象,简化对多个对象的访问。
类型:结构型
使用场景:希望客户端可以忽略组合对象和单个对象的差异时。处理一个树形结构时。
优点:可以清楚地定义分层次的复杂对象,表示对象的全部或部分层次。让客户端忽略层次的差异,方便对整个层次结构进行控制。简化客户端代码。符合开闭原则。
缺点:限制类型时会比较复杂。使设计变得更加抽象。
例子:
public abstract class CatalogComponent {
public void add(CatalogComponent catalogComponent){
throw new UnsupportedOperationException("不支持添加操作");
}
public void remove(CatalogComponent catalogComponent){
throw new UnsupportedOperationException("不支持删除操作");
}
public String getName(CatalogComponent catalogComponent){
throw new UnsupportedOperationException("不支持获取名称操作");
}
public Double getPrice(CatalogComponent catalogComponent){
throw new UnsupportedOperationException("不支持获取价格操作");
}
public void print(){
throw new UnsupportedOperationException("不支持打印操作");
}
}
public class Course extends CatalogComponent {
private String name;
private Double price;
public Course(String name, Double price) {
this.name = name;
this.price = price;
}
@Override
public String getName(CatalogComponent catalogComponent) {
return this.name;
}
@Override
public Double getPrice(CatalogComponent catalogComponent) {
return this.price;
}
@Override
public void print() {
System.out.println("打印:" + name + "价格:" + price);
}
}
public class CourseCatalog extends CatalogComponent {
private String name;
private List<CatalogComponent> list = new ArrayList<>();
public CourseCatalog(String name) {
this.name = name;
}
@Override
public String getName(CatalogComponent catalogComponent) {
return this.name;
}
@Override
public void add(CatalogComponent catalogComponent) {
list.add(catalogComponent);
}
@Override
public void remove(CatalogComponent catalogComponent) {
list.remove(catalogComponent);
}
@Override
public void print() {
System.out.println(this.name);
for (CatalogComponent catalogComponent : list){
System.out.print(" ");
catalogComponent.print();
}
}
}
public class Test {
public static void main(String[] args) {
CatalogComponent linuxCourse = new Course("linux课程" , 10.00);
CatalogComponent javaCatalog = new CourseCatalog("java课程目录");
CatalogComponent javaCourse1 = new Course("springboot源码分析" , 80.00);
CatalogComponent javaCourse2 = new Course("设计模式" , 50.00);
javaCatalog.add(javaCourse1);
javaCatalog.add(javaCourse2);
CatalogComponent mainCatalog = new CourseCatalog("主目录");
mainCatalog.add(linuxCourse);
mainCatalog.add(javaCatalog);
mainCatalog.print();
}
}
类图:
从图中就可以清晰地看出从属关系,从代码也可以看出树形结构,目录和课程都实现同一个接口,根据不同的情况重写响应的方法。结果:
如果打印主目录,现有代码是这个结果,那如果我还想把三级目录再缩进两格,让层次更清晰,就需要在目录类中加很多逻辑代码进行实现,这里用简单的加个level字段举例实现。
public class CourseCatalog extends CatalogComponent {
private String name;
private List<CatalogComponent> list = new ArrayList<>();
private Integer level;
public CourseCatalog(String name, Integer level) {
this.name = name;
this.level = level;
}
@Override
public String getName(CatalogComponent catalogComponent) {
return this.name;
}
@Override
public void add(CatalogComponent catalogComponent) {
list.add(catalogComponent);
}
@Override
public void remove(CatalogComponent catalogComponent) {
list.remove(catalogComponent);
}
@Override
public void print() {
System.out.println(this.name);
for (CatalogComponent catalogComponent : list){
if (this.level != null){
for (int i = 0 ; i < this.level ; i ++){
System.out.print(" ");
}
}
catalogComponent.print();
}
}
}
运行结果:
所以组合模式的缺点也就在此了。
用到设计模式的源码:
Container这个类首先继承了Component,后面在add的时候,增加自己的父类
在addImpl中,也有很多层级判断
再看MixedSqlNode
很典型的组合模式,挑两个类图看看
桥接模式
使抽象部分与它的具体实现分离,是他们都可以独立的变化,实现一定的解耦。通过组合的方式建立两个类之间的联系,而不是继承。
类型:结构型
使用场景:抽象和具体实现之间增加更多的灵活性。一个类存在多个独立的变化维度,并且这些维度都需要独立的进行扩展。不希望使用继承,或因为多层继承导致系统类的个数剧增。
优点:分离抽象部分及其具体实现部分。提高了系统的可扩展性。符合开闭原则,合成复用原则。
缺点:增加了系统的理解和设计难度(因为类之间的关系建立在抽象层,所以一开始就要面对抽象层进行设计和编码)。需要正确的识别出系统中两个独立变化的维度。
例子:
public interface Account {
Account openAccount();
void showAccountType();
}
public class DepositAccount implements Account{
@Override
public Account openAccount() {
System.out.println("打开定期账号");
return new DepositAccount();
}
@Override
public void showAccountType() {
System.out.println("这是定期账号");
}
}
public class SavingAccount implements Account {
@Override
public Account openAccount() {
System.out.println("打开活期账号");
return new SavingAccount();
}
@Override
public void showAccountType() {
System.out.println("这是活期账号");
}
}
public abstract class Bank {
protected Account account;
public Bank(Account account){
this.account = account;
}
abstract Account openAccount();
}
public class ABCBank extends Bank {
public ABCBank(Account account) {
super(account);
}
@Override
Account openAccount() {
System.out.println("打开农业银行账号");
return account;
}
}
public class ICBCBank extends Bank {
public ICBCBank(Account account) {
super(account);
}
@Override
Account openAccount() {
System.out.println("打开工商银行账号");
return account;
}
}
public class Test {
public static void main(String[] args) {
Bank icbcBank1 = new ICBCBank(new DepositAccount());
Account icbcAccount1 = icbcBank1.openAccount();
icbcAccount1.showAccountType();
Bank abcBank1 = new ICBCBank(new SavingAccount());
Account abcAccount1 = abcBank1.openAccount();
abcAccount1.showAccountType();
Bank icbcBank2 = new ICBCBank(new SavingAccount());
Account icbcAccount2 = icbcBank2.openAccount();
icbcAccount2.showAccountType();
Bank abcBank2 = new ICBCBank(new DepositAccount());
Account abcAccount2 = abcBank2.openAccount();
abcAccount2.showAccountType();
}
}
类图:
银行是各自的实现,账号也是各自的实现,这样,就可以各种排列组合,各自扩展自己的银行或账号都不会被影响。
运行结果
这里有个伏笔,运行结果不是非常对:
银行类的openAccount不要自己实现接口,把行为委托给抽象父类注入的Account,这样Account怎么变,这里都不变。
用到设计模式的源码:
java.sql.DriverManager
其中DriverInfo就是对于Driver的封装
接下来看看Driver的实现类
看下mysql的Driver
调用DriverManager的registerDriver完成驱动注册。再看getConnection,用来获取数据库连接
获取完以后返回Connection,Connection接口提供了对数据库操作的各种方法。
所以可以想象成,Driver和DriverManager实现了桥接,Driver接口可以实现各种数据库驱动,DriverManager通过驱动获取连接,并且提供数据库的操作方法,不同的厂商有不同的驱动,实现各自数据库操作的方法。