四、结构型模式介绍
4.1 适配器模式(Adapter Pattern)
当我们需要在现有系统中使用一个类,但是这个类的接口与系统的要求不符合时,适配器模式就可以发挥作用。适配器模式通过引入一个中间件(适配器)来转换原始类的接口,使得原始类可以与系统进行适配。能够帮助解决不同接口之间的兼容性问题,提高系统的可维护性和可扩展性。
适配器模式的角色有:
- 目标(Target)接口
- 适配者(Adaptee)类
- 适配器(Adapter)类
4.1.1 类结构型适配器
--------------------------------------场景模拟----------------------------------------
一个帅哥用华为手机看动物世界,一只小狗如何才能听懂华为手机的再讲什么呢,这时候就需要一个翻译功能,把人类的语言翻译成小狗的语言
----------------------------------------代码实现----------------------------------------
1.定义一个接口 Talk 、以及实现类 HuaWeiPhone (手机播放的汉语)
public interface Talk {
String talk();
}
public class HuaWeiPhone implements Talk {
@Override
public String talk() {
System.out.println("手机--小狗翻译");
String content = "你在狗叫什么";
System.out.println(content);
return content;
}
}
2.定义一个接口 Translator、实现类 DogTalkTranslator (将汉语转换为狗语)
public interface Translator {
String translate(String content);
}
public class DogTalkTranslator implements Translator{
@Override
public String translate(String content) {
if("你在狗叫什么".equals(content)){
return "汪汪汪汪汪汪";
}
if ("走开".equals(content)){
return "汪汪";
}
return "--------";
}
}
3.定义一个适配器类DogTalkAdapter
public class DogTalkAdapter extends DogTalkTranslator implements Talk {
private Talk target;//被适配对象
public DogTalkAdapter(Talk target){
this.target = target;
}
@Override
public String talk() {
String talk = target.talk();
//将汉语转化为狗语
String translate = translate(talk);
System.out.println("狗语:"+translate);
return talk;
}
}
4.测试类测试
public class MainTest {
public static void main(String[] args) {
HuaWeiPhone huaWeiPhone = new HuaWeiPhone();
DogTalkAdapter dogTalkAdapter = new DogTalkAdapter(huaWeiPhone);
huaWeiPhone.talk();
dogTalkAdapter.talk();
}
}
4.1.2 对象结构型适配器
1.定义一个适配器DogTalkAdapter (这里通过组合在对象的形式)
public class DogTalkAdapter implements Talk {
//组合的方式
private Translator translator = new DogTalkTranslator();
private Talk target;//被适配对象
public DogTalkAdapter(Talk target){
this.target = target;
}
@Override
public String talk() {
String play = target.talk();
//转换语言
String translate = translator.translate(play);
System.out.println("狗语:"+translate);
return play;
}
}
2.测试类测试
public class MainTest {
public static void main(String[] args) {
DogTalkAdapter adapter = new DogTalkAdapter(new HuaWeiPhone());
adapter.talk();
}
}
4.2 桥接模式(Bridge Pattern)
它将抽象部分与它的实现部分分离开来,使它们都可以独立地变化。这种模式就像一座桥,连接了两个不同的部分,让它们可以相互协作
--------------------------------------场景模拟----------------------------------------
假如有一个帅哥,他在德国和英国都有一栋房子,那么他可以选择骑自行车或者骑摩托车去,这时候的摩托车还有自行车就相当于一个连接方法,将帅哥和房子通过某种方式连接到一起
----------------------------------------代码实现----------------------------------------
1.定义 两个工具类 Bicycle 、 Motorcycle
public class Bicycle extends AbstractVehicle {
@Override
String getHome() {
return "自行车:"+traffic.getTrafficInfo();
}
}
public class Motorcycle extends AbstractVehicle {
@Override
String getHome() {
return "摩托车:"+traffic.getTrafficInfo();
}
}
2.定了两个目的地England 、Germany
public class England extends AbstractTraffic {
public England(String speed, String time) {
super(speed, time);
}
}
public class Germany extends AbstractTraffic {
public Germany(String speed, String time) {
super(speed, time);
}
}
3.定义一个抽象类AbstractTraffic (定义了车辆的速度和时间)
public abstract class AbstractTraffic {
private String speed;
private String time;
public AbstractTraffic(String speed, String time){
this.speed = speed;
this.time = time;
}
String getTrafficInfo(){
return "速度:"+ speed +"==>"+"时间:"+ time;
}
}
4.定义一个抽象类AbstractVehicle (桥接的地方)
public abstract class AbstractVehicle {
AbstractTraffic traffic; //分离渠道【桥接的关注点】
abstract String getHome();
public void setTraffic(AbstractTraffic traffic) {
this.traffic = traffic;
}
}
5.测试类测试
public class MainTest {
public static void main(String[] args) {
Motorcycle motorcycle = new Motorcycle();
motorcycle.setTraffic(new Germany("180km/h","99分钟"));
System.out.println(motorcycle.getHome());
motorcycle.setTraffic(new England("180km/h","88分钟"));
System.out.println(motorcycle.getHome());
Bicycle bicycle = new Bicycle();
bicycle.setTraffic(new Germany("160km/h","77分钟"));
System.out.println(bicycle.getHome());
bicycle.setTraffic(new England("160km/h","66分钟"));
System.out.println(bicycle.getHome());
}
}
4.3 装饰器模式(Decorator/Wrapper(包装) Pattern)
装饰模式就像是给一个物体穿上各种漂亮的外衣,让它看起来更独特😉 它是一种动态地给一个对象添加一些额外的职责的设计模式。
--------------------------------------场景模拟----------------------------------------
假如一个帅哥在一个直播平台,但是没有什么人观看,所有它就开了一个直播特效,把自己变得很神秘,最后吸引人来观看,这种特点就是装饰
----------------------------------------代码实现----------------------------------------
1.定义一个接口XiongMaoTv (直播平台)
public interface XiongMaoTv {
void show();
}
2.定义一个HandsomeBoyShow类 (帅哥没开特效直播)
public class HandsomeBoyShow implements XiongMaoTv {
@Override
public void show() {
System.out.println("帅哥直播,没开直播特效:观众人数极少");
}
}
3.定义一个装饰器接口XiongMaoTvDecorator (提供特效接口)
public interface XiongMaoTvDecorator extends XiongMaoTv {
//开启特效
void VFX();
}
4.定义一个装饰器实现类VFXDecorator (具体实现特效装饰功能)
public class VFXDecorator implements XiongMaoTvDecorator {
private XiongMaoTv xiongMaoTv;
public VFXDecorator(XiongMaoTv xiongMaoTv){
this.xiongMaoTv = xiongMaoTv;
}
@Override
public void show() {
//开启特性
VFX();
//开始直播
xiongMaoTv.show();
}
/**
* 定义的增强功能
*/
@Override
public void VFX() {
System.out.println("帅哥开启了直播特效:观众人数蹭蹭往上涨--");
}
}
5.测试类测试
public class MainTest {
public static void main(String[] args) {
//没开直播特效
XiongMaoTv xiongMaoTv = new HandsomeBoyShow();
xiongMaoTv.show();
System.out.println("------------------------");
//开了直播特效
VFXDecorator decorator = new VFXDecorator(xiongMaoTv);
decorator.show();
}
}
4.4 外观模式(Facade Pattern)
外观模式的主要思想是将系统的复杂部分隐藏在一个外观对象后面,这个外观对象提供了一个简化的接口,用户只需与这个接口进行交互,而无需了解系统内部的复杂结构和操作。这样可以提高系统的易用性和可维护性。
--------------------------------------场景模拟----------------------------------------
有一个帅哥想去理发,又想去打台球,又想去吃饭,但是这三个地方都相隔很远,纠结要不要去,这时候帅哥看到一则消息,有一个地方同时满足了他这三个要求,他就果断去了
----------------------------------------代码实现----------------------------------------
1.定义Canteen、GoodPlace、Barbershop (吃饭,理发,打台球)
public class Canteen {
public void eat(String name){
System.out.println(name+"吃饭-------");
}
}
public class Barbershop {
public void action(String name){
System.out.println(name + "理发-----");
}
}
public class BilliardParlor {
public void play(String name){
System.out.println(name+"打台球-------");
}
}
2.定义一个门面类GoodPlace (同时满足三种需求)
public class GoodPlace {
Barbershop barbershop = new Barbershop();
Canteen canteen = new Canteen();
BilliardParlor billiardParlor = new BilliardParlor();
public void handle(String name){
barbershop.action(name);
canteen.eat(name);
billiardParlor.play(name);
}
}
3.测试类测试
public class MainTest {
public static void main(String[] args) {
GoodPlace goodPlace = new GoodPlace();
goodPlace.handle("帅哥");
}
}
4.5 组合模式(Composite Pattern)
允许你将对象组合成树状结构,并以一致的方式处理单个对象和组合对象。
--------------------------------------场景模拟----------------------------------------
就是类似一些电商网站中,一些菜单的树形结构
这时候三级菜单就组合在二级菜单里面,二级菜单又组合在一级菜单里面
----------------------------------------代码实现----------------------------------------
1.定义 Menu 类 (菜单的各种属性)
@Data
public class Menu {
private Integer id;
private String menuName;
public Menu(Integer id,String menuName){
this.id = id;
this.menuName = menuName;
}
//组合模式关注点
private List<Menu> childs = new ArrayList<>();
//提供添加层级的方法
void addChildMenu(Menu menu){
childs.add(menu);
}
//层级遍历方法
void printMenu(){
System.out.println(menuName);
if(childs.size() > 0){
for (Menu child : childs) {
child.printMenu();
}
}
}
}
2.测试类测试
public class MainTest {
public static void main(String[] args) {
Menu menu = new Menu(1, "电商树形结构");
Menu one = new Menu(2, "一级分类:手机");
menu.addChildMenu(one);
one.addChildMenu(new Menu(6,"二级分类:手机通讯"));
one.addChildMenu(new Menu(7,"二级分类:手机配件"));
Menu two = new Menu(3, "一级分类:电脑");
menu.addChildMenu(two);
two.addChildMenu(new Menu(4,"二级分类:电脑配件"));
two.addChildMenu(new Menu(5,"二级分类:电脑整机"));
menu.printMenu();
}
}
4.6 享元模式(Flyweight Pattern)
一种优化性能的设计模式,它用于减少对象的创建数量,从而节省内存和提高系统性能。 享元模式的核心思想是将具有相同内部状态的对象共享使用。通过创建一个共享对象池,当需要创建对象时,首先检查池中是否已经存在具有相同状态的对象,如果存在则直接返回这个对象,而不是创建新的对象。
享元模式中的角色
- Flyweight: 抽象享元类
- ConcreteFlyweight: 具体享元类
- UnsharedConcreteFlyweight: 非共享具体享元类
- FlyweightFactory: 享元工厂类;简单工厂
--------------------------------------场景模拟----------------------------------------
比如在一些数据库连接池中,我们要频繁的连接数据库,这时候如果每次连接数据库都开启一个数据库连接,就会很浪费性能,我们可以把数据库连接这个功能放入数据池中,谁需要就去调用即可
----------------------------------------代码实现----------------------------------------
1.定义一个数据连接工具类JDBCConnect
@AllArgsConstructor
public class JDBCConnect extends AbstractWaitressFlyweight {
String connectId;
String connectName;
@Override
void begin() {
System.out.println("数据连接id:"+connectId+";"+ connectName +" 开始连接连接数据库");
//改变外部状态
this.canService = false;
}
@Override
void end() {
System.out.println("数据连接id:"+connectId+";"+ connectName +"关闭数据库连接");
this.canService = true;
}
}
2.定义一个数据连接池DatabaseConnectionPool
public class DatabaseConnectionPool {
private static Map<String,AbstractWaitressFlyweight> pool = new HashMap<>();
//享元,池子中有对象
static {
JDBCConnect waitress =
new JDBCConnect("10086","数据连接10086号");
JDBCConnect waitress2 =
new JDBCConnect("12345","数据连接12345号");
pool.put(waitress.connectId,waitress);
pool.put(waitress2.connectId,waitress2);
}
public static AbstractWaitressFlyweight getWaitress(String name){
AbstractWaitressFlyweight flyweight = pool.get(name);
if(flyweight == null){
for (AbstractWaitressFlyweight value : pool.values()) {
//当前共享对象能否是否
if(value.isCanService()){
return value;
}
};
return null;
}
return flyweight;
}
}
3.定义一个抽象类AbstractWaitressFlyweight
public abstract class AbstractWaitressFlyweight {
boolean canService = true;//能否连接
//正在服务。 享元的不可共享属性留给外部进行改变的接口
abstract void begin();
//服务完成。 享元的不可共享属性留给外部进行改变的接口
abstract void end();
public boolean isCanService() {
return canService;
}
}
4.测试类测试
public class MainTest {
public static void main(String[] args) {
AbstractWaitressFlyweight abstractWaitressFlyweight = DatabaseConnectionPool.getWaitress("");
abstractWaitressFlyweight.begin();
System.out.println(abstractWaitressFlyweight);
AbstractWaitressFlyweight ab = DatabaseConnectionPool.getWaitress("");
ab.begin();
System.out.println(ab);
ab.end();
AbstractWaitressFlyweight aw = DatabaseConnectionPool.getWaitress("");
System.out.println(aw);
}
}
特别的需要注意的是享元模式与单例模式的区别就在于,我们那到的实例,享元模式是他的本体,而单例模式拿到的是克隆体