设计模式之结构型模式
结构型模式
关注类与对象的组合。将不同的类结合在一起形成结构。
1. 适配器模式(Adapter)
类似于现在的手机充电器。电源是220V的,手机需要5V的,通过适配器进行转化,让手机充上电。以及三脚插座与两脚插座的转换器。
代码实例
//家用电源提供220V电压
public class HomeBattery {
int supply() {
return 220;
}
}
//USB数据线只支持5V电压
public class UsbLine {
void charge(int volt) {
if (volt != 5) {
throw new IllegalArgumentException("只能接受5V电压");
}
System.out.println("正常充电");
}
}
//适配器,让家用电压降低至5V再充电
public class Adapter {
int convert(int homeVolt) {
int chargeVolt = homeVolt - 215;
return chargeVolt;
}
}
public class User {
public static void main(String[] args) {
HomeBattery battery = new HomeBattery();
int homevolt = battery.supply();
System.out.println("电压是:" + homevolt + "V");
//直接充电是充不了的,使用适配器之后就可以充电了
Adapter adapter = new Adapter();
int chargeVolt = adapter.convert(homevolt);
UsbLine usbLine = new UsbLine();
usbLine.charge(chargeVolt);
}
}
2. 桥接模式(Bridge)
将抽象部分与它的实现部分分离,使它们都可以独立地变化
代码实例
//形状接口
public interface Shape {
void draw();
}
public class Rectangle implements Shape {
private Color color;
public void setColor(Color color) {
this.color = color;
}
@Override
public void draw() {
System.out.println("画" + color.getColor() + "矩形");
}
}
public class Round implements Shape {
private Color color;
public void setColor(Color color) {
this.color = color;
}
@Override
public void draw() {
System.out.println("画" + color.getColor() + "圆形");
}
}
//颜色接口
public interface Color {
String getColor();
}
public class Blue implements Color{
@Override
public String getColor() {
return "蓝色";
}
}
public class Yellow implements Color{
@Override
public String getColor() {
return "黄色";
}
}
测试
public class Demo {
public static void main(String[] args) {
Rectangle rectangle = new Rectangle();
rectangle.setColor(new Blue());
rectangle.draw();
Round round = new Round();
round.setColor(new Yellow());
round.draw();
}
}
结果
画蓝色矩形
画黄色圆形
可以发现,如果不适用桥接模式,那么每个图形都要实现2个颜色,就会有4个类(2个图像和2种颜色),导致管理复杂。桥接模式适用于两个或多个同等级的接口(本例是图形的颜色和形状)
3. 组合模式(Composite)
把一组相似的对象当做一个单一的对象
代码实例
如现在有两个类,管理员和职工
public class Manager {
// 职位
private String position;
// 工作内容
private String job;
// 管理员的管理者
private List<Manager> managers = new ArrayList<>();
// 管理的职员
private List<Employee> employees = new ArrayList<>();
public Manager(String position, String job) {
this.position = position;
this.job = job;
}
public void addManager(Manager manager) {
managers.add(manager);
}
public void removeManager(Manager manager) {
managers.remove(manager);
}
public void addEmployee(Employee employee) {
employees.add(employee);
}
public void removeEmployee(Employee employee) {
employees.remove(employee);
}
// 做自己的本职工作
public void work() {
System.out.println("我是" + position + ",我正在" + job);
}
// 检查下属
public void check() {
work();
for (Employee employee : employees) {
employee.work();
}
for (Manager manager : managers) {
manager.check();
}
}
}
public class Employee {
// 职位
private String position;
// 工作内容
private String job;
public Employee(String position, String job) {
this.position = position;
this.job = job;
}
// 做自己的本职工作
public void work() {
System.out.println("我是" + position + ",我正在" + job);
}
}
会发现这两个类很多部分都是重复的,那么可以用一个抽象类提取重复部分,让这两个类都继承抽象类。修改后的代码如下
public abstract class Component {
// 职位
private String position;
// 工作内容
private String job;
public Component(String position, String job) {
this.position = position;
this.job = job;
}
// 做自己的本职工作
public void work() {
System.out.println("我是" + position + ",我正在" + job);
}
abstract void addComponent(Component component);
abstract void removeComponent(Component component);
abstract void check();
}
public class Manager extends Component {
List<Component> list = new ArrayList<>();
public Manager(String position, String job) {
super(position, job);
}
@Override
void addComponent(Component component) {
list.add(component);
}
@Override
void removeComponent(Component component) {
list.remove(component);
}
@Override
void check() {
work();
for (Component component : list) {
component.check();
}
}
}
public class Employee extends Component {
public Employee(String position, String job) {
super(position, job);
}
@Override
void addComponent(Component component) {
System.out.println("无权限");
}
@Override
void removeComponent(Component component) {
System.out.println("无权限");
}
@Override
void check() {
work();
}
}
在调用的时候只需要调用抽象类即可。
public class Demo {
public static void main(String[] args) {
Component boss = new Manager("老板", "唱怒放的生命");
Component HR = new Employee("人力资源", "聊微信");
Component PM = new Manager("产品经理", "不知道干啥");
Component CFO = new Manager("财务主管", "看剧");
Component CTO = new Manager("技术主管", "划水");
Component UI = new Employee("设计师", "画画");
Component operator = new Employee("运营人员", "兼职客服");
Component webProgrammer = new Employee("程序员", "学习设计模式");
Component backgroundProgrammer = new Employee("后台程序员", "CRUD");
Component accountant = new Employee("会计", "背九九乘法表");
Component clerk = new Employee("文员", "给老板递麦克风");
boss.addComponent(HR);
boss.addComponent(PM);
boss.addComponent(CFO);
PM.addComponent(UI);
PM.addComponent(CTO);
PM.addComponent(operator);
CTO.addComponent(webProgrammer);
CTO.addComponent(backgroundProgrammer);
CFO.addComponent(accountant);
CFO.addComponent(clerk);
boss.check();
}
}
4. 装饰器模式(Decorator)
在不改变原有类的基础上增强类的原有功能或者为一个类添加新功能。
增强原有功能
public interface Money {
int getMoney();
}
public class AliPay implements Money {
@Override
public int getMoney() {
return 100;
}
}
public class Stock implements Money {
public final Money aliPay;
public Stock(Money aliPay) {
this.aliPay = aliPay;
}
@Override
public int getMoney() {
return aliPay.getMoney() + 20;
}
}
public class Demo {
public static void main(String[] args) {
Money money = new AliPay();
System.out.println(money.getMoney());
//money经过stock增强之后变多了
Money moneyWithStock = new Stock(money);
System.out.println(moneyWithStock.getMoney());
}
}
装饰器没有改变接口,也没有新增方法,所以被称之为透明装饰模式,特点是可以无限装饰
添加功能
//新增装饰器接口,因为是对原有的接口的扩展,所以需要继承原有类
public interface Bank extends Money {
void loan();
}
//实现接口
public class WeChat implements Bank {
private final Money money;
public WeChat(Money money) {
this.money = money;
}
@Override
public void loan() {
System.out.println("借钱");
}
@Override
public int getMoney() {
return money.getMoney()+10;
}
}
public class Demo {
public static void main(String[] args) {
Money money = new AliPay();
System.out.println(money.getMoney());
Bank moneyCanLoad = new WeChat(money);
System.out.println(moneyCanLoad.getMoney());
moneyCanLoad.loan();
}
}
这样就达到了新增功能的目的。客户端要使用新方法,必须知道装饰类,但是被装饰者是对客户端不可见的,所以被称之为半透明装饰模式。
Java I/O 设计框架用的就是装饰器模式,在基础的InputStream和OutputStream上做功能的增强。
装饰器模式与适配器模式的区别
这两者很容易混淆
- 适配器模式仅用于改变接口
- 装饰器模式不改变原有接口,只是增强或者添加功能。
5. 外观模式(Facade)
将子系统封装起来,提供一个简洁的接口供外界调用。MVC架构就是这种设计模式的体现,Controller供其他人调用。因为太过于简单,不举代码实例了。
6. 享元模式(Flyweight)
使用共享支持大量细粒度对象。 使用场景:需要使用大量的对象。 魔法药店里有大量的药 ```java public interface Potion { void drink(); }
public class HealingPotion implements Potion { @Override public void drink() { // drink } }
public class HolyWaterPotion implements Potion{
@Override
public void drink() {
//drink
}
}
public class InvisibilityPotion implements Potion{ @Override public void drink() { // } }
```java
public class PotionFactory {
private final Map<PotionType, Potion> potions;
public PotionFactory() {
potions = new EnumMap<>(PotionType.class);
}
Potion createPotion(PotionType type) {
var potion = potions.get(type);
if (potion == null) {
switch (type) {
case HEALING -> {
potion = new HealingPotion();
potions.put(type, potion);
}
case HOLY_WATER -> {
potion = new HolyWaterPotion();
potions.put(type, potion);
}
case INVISIBILITY -> {
potion = new InvisibilityPotion();
potions.put(type, potion);
}
default -> {
}
}
}
return potion;
}
}
public class AlchemistShop {
private final List<Potion> topShelf;
private final List<Potion> bottomShelf;
public AlchemistShop() {
var factory=new PotionFactory();
topShelf=List.of(
factory.createPotion(PotionType.INVISIBILITY),
factory.createPotion(PotionType.INVISIBILITY),
factory.createPotion(PotionType.STRENGTH),
factory.createPotion(PotionType.HEALING),
factory.createPotion(PotionType.INVISIBILITY),
factory.createPotion(PotionType.STRENGTH),
factory.createPotion(PotionType.HEALING),
factory.createPotion(PotionType.HEALING)
);
bottomShelf =List.of(
factory.createPotion(PotionType.POISON),
factory.createPotion(PotionType.POISON),
factory.createPotion(PotionType.POISON),
factory.createPotion(PotionType.HOLY_WATER),
factory.createPotion(PotionType.HOLY_WATER)
);
}
public final List<Potion> getTopShelf() {
return List.copyOf(this.topShelf);
}
public final List<Potion> getBottomShelf() {
return List.copyOf(this.bottomShelf);
}
public void drinkPotions() {
LOGGER.info("Drinking top shelf potions\n");
topShelf.forEach(Potion::drink);
LOGGER.info("Drinking bottom shelf potions\n");
bottomShelf.forEach(Potion::drink);
}
}
7. 代理模式(Proxy)
给某个对象提供代理,由代理控制对原对象的引用。(有些时候直接访问会比较麻烦,那么可以访问该对象的代理对象来操作)
静态代理
public interface Http {
void request(String sendData);
void onSuccess(String receivedData);
}
public class HttpUtil implements Http {
@Override
public void request(String sendData) {
System.out.println("网络请求中...");
}
@Override
public void onSuccess(String receivedData) {
System.out.println("网络请求完成。");
}
}
//代理类
public class HttpProxy implements Http {
private final HttpUtil httpUtil;
public HttpProxy(HttpUtil httpUtil) {
this.httpUtil = httpUtil;
}
@Override
public void request(String sendData) {
System.out.println("发送" + sendData);
httpUtil.request(sendData);
}
@Override
public void onSuccess(String receivedData) {
System.out.println("收到" + receivedData);
httpUtil.onSuccess(receivedData);
}
}
public class Demo {
public static void main(String[] args) {
HttpProxy httpProxy = new HttpProxy(new HttpUtil());
httpProxy.request("消息");
httpProxy.onSuccess("测试");
}
}
结果
发送消息
网络请求中...
收到测试
网络请求完成。
动态代理
和静态代理的区别是,如果代理对象有很多方法,那么静态代理需要全部实现这些方法,而动态代理可以选择性的实现部分。
//动态代理的实现比较麻烦,需要使用反射
public class HttpProxy implements InvocationHandler {
private HttpUtil httpUtil;
public Http getInstance(HttpUtil httpUtil) {
this.httpUtil = httpUtil;
//专门用于动态代理的方法
return (Http) Proxy.newProxyInstance(httpUtil.getClass().getClassLoader(), httpUtil.getClass().getInterfaces(), this);
}
@Override
public Object invoke(Object o, Method method, Object[] args) throws Throwable {
Object result = null;
if (method.getName().equals("request")) {
System.out.println("发送数据" + args[0]);
result = method.invoke(httpUtil, args);
} else if (method.getName().equals("onSuccess")) {
System.out.println("收到数据" + args[0]);
result = method.invoke(httpUtil, args);
}
return result;
}
}