设计模式-结构型模式
文章目录
- 一、代理模式
- 1.静态代理
- 2.JDK动态代理
- 3.CGLib动态代理
- 4.三种代理对比
 
- 二、适配器模式
- 1.类适配器模式
- 2.对象适配器模式
 
- 三、装饰者模式
- 静态代理和装饰者的区别
 
- 四、桥接模式
- 五、外观模式
- 六、组合模式
- 七、享元模式
结构性模式描述如何将类或对象按某种布局组成更大的结构。分为:类结构型模式:采用继承机制来组织接口和类对象结构型模式:采用组合或聚合来组合对象。耦合度更低,满足合成复用原则
一、代理模式
由于某些原因需要给某对象提供一个代理以控制对该对象的访问。这时,访问对象不适合或不能直接引用目标对象,
代理对象作为访问对象和目标对象之间的中介。java中的代理按照代理类生成时机不同分为静态代理和动态代理。
静态代理:代理类在编译期生成
动态代理:在运行时动态生成。又分为JDK代理和CGLib代理角色:抽象主题类:通过接口或抽象类声明真实主题和代理对象实现的业务方法真实主题类:实现了抽象主题中的具体业务,是代理对象所代表的真实对象,是最终要引用的对象代理类:提供了与真实主题相同的接口,其内部含有对真实主题的引用,它可以访问、控制或拓展真实主题的功能优点:代理模式在客户端与目标对象之间起到一个中介作用和保护目标对象的作用代理对象可以扩展目标对象的功能代理模式能将客户端与目标对象分离,在一定程度上降低了系统的耦合度缺点:增加了系统的复杂度使用场景:a.远程代理:本地服务通过网络请求远程服务。为了实现本地到远程的通信,我们需要实现网络通信,处理其中可能的异常。为了良好的代码设计和可维护性,我们将网络通信部分隐藏起来,只暴露给本地服务一个接口,通过该接口即可访问远程服务提供的功能,而不必过多关心通信部分的细节b.防火墙代理:将浏览器配置成使用代理功能时,防火墙就将浏览器的请求转发给互联网;当互联网返回响应时,代理服务器再把它转给浏览器c.保护代理:控制对一个对象的访问,如果需要,可以给不同的用户提供不同级别的使用权限
1.静态代理
/*** 抽象主题类 : 买火车票的接口*/
public interface SellTickets {void sell();}/*** 真实主题类 : 火车站类*/
public class TrainStation implements SellTickets{@Overridepublic void sell() {System.out.println("火车站卖票");}
}/*** 代理类 : 代售点类*/
public class ProxyPoint implements SellTickets{//声明火车站类对象private TrainStation station = new TrainStation();@Overridepublic void sell() {System.out.println("这里是代售点,需要10块钱服务费");station.sell();}
}public class Client {public static void main(String[] args) {ProxyPoint proxyPoint = new ProxyPoint();proxyPoint.sell();}}
2.JDK动态代理
java中提供了一个动态代理类Proxy,Proxy中提供了 newProxyInstance() 来获取代理对象执行流程:
在测试类中通过对象调用sell()方法
根据多态的特性,执行的是代理类($Proxy0)中的sell()方法
代理类($Proxy0)中的sell()方法中又调用了InvocationHandler接口的自实现类对象的invake方法
invake方法通过反射执行了真实对象所属类(TrainStation)中的sell()方法
/*** 抽象主题类 : 买火车票的接口*/
public interface SellTickets {void sell();}/*** 真实主题类 : 火车站类*/
public class TrainStation implements SellTickets {@Overridepublic void sell() {System.out.println("火车站卖票");}
}/*** 获取代理对象的工厂类* 代理类也实现了对应的接口*/
public class ProxyFactory {//声明目标对象private TrainStation station = new TrainStation();public SellTickets getProxyObject(){/*** ClassLoader loader : 类加载器,用于加载代理类,可以通过目标对象获取类加载器* Class<?>[] interfaces : 代理类实现的接口的字节码对象* InvocationHandler h : 代理对象的调用处理程序*/SellTickets proxyInstance = (SellTickets) Proxy.newProxyInstance(station.getClass().getClassLoader(),station.getClass().getInterfaces(),new InvocationHandler() {/*** proxy : 代理对象 和proxyInstance是同一个对象,在invoke中基本不用* method : 对接口中的方法进行封装的method对象* args : 调用方法的实际参数* 返回值 : 方法的返回值*/@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("代售点收取10块钱服务费");//执行目标对象的方法Object obj = method.invoke(station, args);return obj;}});return proxyInstance;}}public class Client {public static void main(String[] args) {//获取代理对象ProxyFactory factory = new ProxyFactory();SellTickets proxy = factory.getProxyObject();proxy.sell();}}
3.CGLib动态代理
需要引入第三方jar包为没有实现接口的类提供代理CGLib底层采用ASM字节码生成框架,使用字节码技术
/*** 代理对象工厂*/
public class ProxyFactory implements MethodInterceptor {private TrainStation station = new TrainStation();public TrainStation getProxyObject() {//类似于JDK代理中的Proxy类Enhancer enhancer = new Enhancer();//设置父类的字节码对象enhancer.setSuperclass(TrainStation.class);//设置回调函数enhancer.setCallback(this);//创建代理对象TrainStation proxyObject = (TrainStation) enhancer.create();return proxyObject;}@Overridepublic Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy)throws Throwable {System.out.println("代售点坑人");//调用目标对象的方法Object obj = method.invoke(station, objects);return obj;}
}/*** 真实主题类 : 火车站类*/
public class TrainStation{public void sell() {System.out.println("火车站卖票");}}public class Client {public static void main(String[] args) {//创建代理工厂对象ProxyFactory factory = new ProxyFactory();//获取代理对象TrainStation proxy = factory.getProxyObject();//调用代理对象中的sell卖票proxy.sell();}}
4.三种代理对比
JDK和CGLIB:
CGLIB底层采用ASM字节码生成框架,使用字节码技术生成代理类,在JDK1.6之前比使用java反射效率要高。
但是CGLIB不能对声明为final的类或者方法进行代理,因为CGLIB原理是动态生成被代理类的子类。
在JDK1.8的时候,JDK的效率高于CGLIB。
所以如果有接口就使用JDK代理,没有的话使用CGLIB代理静态代理和动态代理:
动态代理最大的好处就是接口中声明的所有方法都被转移到调用处理器一个集中的方法中处理。
这样在接口方法数量比较多的时候,我们可以灵活处理,而不需要像静态代理那样每一个方法进行中转
如果接口增加一个方法,静态代理模式除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法。
增加了代码维护的复杂度,而动态代理不会出现该问题
二、适配器模式
将一个类的接口转换成客户希望的另外一个接口,使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。分类:类适配器:耦合度较高,要求了解现有组件库中的相关组件的内部结构,所以应用相对较少对象适配器:结构:目标接口:当前系统业务所期待的接口,它可以是抽象类或接口适配者类:它是被访问和适配的现存组件库中的组件接口适配器类:它是一个转换器,通过继承或引用适配者的对象,把适配者接口转换成目标接口,让客户按目标接口的访问格式访问适配者应用场景:以前开发地系统存在满足新系统功能需求的类,但是其接口同新系统的接口不一致使用第三方提供的组件,但组件接口定义和自己要求的接口定义不同
1.类适配器模式
实现方式:定义一个适配器类来实现当前系统的业务接口,同时又继承现有组件库中已经存在的组件违背了合成复用原则,在客户类有一个接口规范的情况下可以使用,如果没有就不能使用
/*** 目标接口*/
public interface SDCard {String readSD();void writeSD(String msg);
}/*** 具体类*/
public class SDCardImpl implements SDCard{@Overridepublic String readSD() {return "从SD卡读取数据";}@Overridepublic void writeSD(String msg) {System.out.println("向SD卡写入数据 : " + msg);}
}/*** 适配者类的接口*/
public interface TFCard {String read();void write(String msg);}/*** 适配者类*/
public class TFCardImpl implements TFCard{@Overridepublic String read() {return "TFCard读取了数据";}@Overridepublic void write(String msg) {System.out.println("TFCard写入了数据 : " + msg);}
}/*** 适配器类*/
public class SDAdapterTF extends TFCardImpl implements SDCard{@Overridepublic String readSD() {System.out.println("adapter read TFCard");return read();}@Overridepublic void writeSD(String msg) {System.out.println("adapter write TFCard");write(msg);}
}/*** 计算机类*/
public class Computer {//从SD卡中读取数据public String readSD(SDCard sdCard){if (sdCard == null){throw new NullPointerException("SDCard is not null");}return sdCard.readSD();}}public class Client {public static void main(String[] args) {Computer computer = new Computer();String s = computer.readSD(new SDCardImpl());System.out.println(s);System.out.println("================");String msg = computer.readSD(new SDAdapterTF());System.out.println(msg);}}
2.对象适配器模式
不再去继承适配者类,而且聚合
/*** 适配器类*/
public class SDAdapterTF implements SDCard {//声明适配者类private TFCard tfCard;public SDAdapterTF(TFCard tfCard){this.tfCard = tfCard;}@Overridepublic String readSD() {System.out.println("adapter read TFCard");return tfCard.read();}@Overridepublic void writeSD(String msg) {System.out.println("adapter write TFCard");tfCard.write(msg);}
}
三、装饰者模式
指在不改变现有对象结构的情况下,动态地给该对象增加一些功能的模式结构:抽象构件角色:定义一个抽象接口以规范准备接收附加责任的对象具体构件角色:实现抽象构件,通过装饰角色为其添加一些职责抽象装饰角色:继承或实现抽象构件,并包含具体构件的实例,可以通过其子类扩展具体构件的功能具体装饰角色:实现抽象装饰的相关方法,并给具体构件对象添加附加的责任优点:1.装饰者模式可以带来比继承更加灵活性的扩展功能,使用更加方便,可以通过组合不同的装饰者对象来获取具有不同行为状态的多样化结果。装饰者模式比继承更具良好的扩展性,完美的遵循开闭原则,继承是静态的附加责任,装饰者则是动态地附加责任2.装饰类和被装饰类可以独立发展,不会相互耦合,装饰者模式是继承的一个替代模式,装饰模式可以动态扩展一个实现类的功能使用场景:1.当不能采用继承的方式对系统进行扩展或者采用继承不利于系统扩展和维护时不能采用继承:系统中存在大量独立的扩展,为支持每一种组合将产生大量的子类类被定义为final2.在不影响其它对象的情况下,以动态、透明的方式给单个对象添加职责3.当对象的功能要求可以动态地添加,也可以动态地撤销时/*** 抽象构件角色 :快餐类*/
public abstract class FastFood {private float price;private String desc;public FastFood(){}public FastFood(float price, String desc) {this.price = price;this.desc = desc;}public float getPrice() {return price;}public void setPrice(float price) {this.price = price;}public String getDesc() {return desc;}public void setDesc(String desc) {this.desc = desc;}public abstract float cost();
}/*** 具体构件角色 :炒饭*/
public class FriedRice extends FastFood{private static final float price = 10;private static final String desc = "炒饭";public FriedRice(){super(price,desc);}@Overridepublic float cost() {return super.getPrice();}
}/*** 具体构件角色 :炒面*/
public class FiredNoodles extends FastFood{private static final float price = 12;private static final String desc = "炒面";public FiredNoodles(){super(price,desc);}@Overridepublic float cost() {return super.getPrice();}
}/*** 抽象装饰角色*/
public abstract class Garnish extends FastFood{//声明快餐类的变量private FastFood fastFood;public Garnish(float price, String desc, FastFood fastFood) {super(price, desc);this.fastFood = fastFood;}public FastFood getFastFood() {return fastFood;}public void setFastFood(FastFood fastFood) {this.fastFood = fastFood;}
}/*** 具体装饰者角色*/
public class Egg extends Garnish{private static final float price = 1;private static final String desc = "鸡蛋";public Egg(FastFood fastFood){super(price,desc,fastFood);}@Overridepublic float cost() {//计算价格return price + super.getFastFood().cost();}@Overridepublic String getDesc() {return desc + super.getFastFood().getDesc();}
}/*** 具体装饰者角色*/
public class Bacon extends Garnish{private static final float price = 3;private static final String desc = "培根";public Bacon(FastFood fastFood){super(price,desc,fastFood);}@Overridepublic float cost() {//计算价格return price + super.getFastFood().cost();}@Overridepublic String getDesc() {return desc + super.getFastFood().getDesc();}
}public class Test {public static void main(String[] args) {//炒饭FastFood food = new FriedRice();System.out.println(food.getDesc() + "  " + food.cost() + "元");System.out.println("---------------------");//加鸡蛋food = new Egg(food);System.out.println(food.getDesc() + "  " + food.cost() + "元");System.out.println("---------------------");//再加鸡蛋food = new Egg(food);System.out.println(food.getDesc() + "  " + food.cost() + "元");System.out.println("---------------------");//再加培根food = new Bacon(food);System.out.println(food.getDesc() + "  " + food.cost() + "元");}}
静态代理和装饰者的区别
相同点:1.都要实现与目标类相同的业务接口2.在两个类中都要声明目标对象3.都可以在不修改目标类的前提下增强目标方法不同点:1.目的不同:装饰者是为了增强目标对象静态代理是为了保护和隐藏目标对象2.获取目标对象构建的地方不同:装饰者是由外界传递进来,可以通过构造方法传递静态代理是在代理类内部创建,以此来隐藏目标对象
四、桥接模式
将抽象与实现分离,使他们可以独立变化。它是用组合关系代替继承关系来实现,从而降低了抽象和实现这两个
可变维度的耦合度结构:抽象化角色:定义抽象类,并包含一个对实现化对象的引用扩展抽象化角色:是抽象化角色的子类,实现父类中的业务方法,并通过组合关系调用实现化角色中的业务方法实现化角色:定义实现化角色的接口,供扩展抽象化角色调用具体实现化角色:给出实现化角色接口的具体实现优点:1.提高了系统的可扩充性,在两个变化维度中任意扩展一个维度,都不需要修改原有系统2.实现细节对客户透明使用场景:1.当一个类存在两个独立变化的维度,且这两个维度都需要进行扩展时2.当一个系统不希望使用继承或其它多层次继承导致类的个数急剧增加时3.当一个系统需要在构件的抽象化和具体角色之间增加更多的灵活性时。避免在两个层次之间建立静态的继承联系,通过桥接模式可以使他们在抽象层建立一个关联关系
/*** 实现化角色*/
public interface VideoFile {//解码void decode(String fileName);}/*** 具体实现化角色*/
public class AVIFile implements VideoFile{@Overridepublic void decode(String fileName) {System.out.println("avi视频文件 : " + fileName);}
}/*** 具体实现角色*/
public class RMVBFile implements VideoFile{@Overridepublic void decode(String fileName) {System.out.println("rmvb视频文件 : " + fileName);}
}/*** 抽象化角色*/
public abstract class OpratingSystem {public VideoFile videoFile;public OpratingSystem(VideoFile videoFile){this.videoFile = videoFile;}public abstract void play(String fileName);}/*** 扩展抽象化角色*/
public class Windows extends OpratingSystem{public Windows(VideoFile videoFile) {super(videoFile);}@Overridepublic void play(String fileName) {videoFile.decode(fileName);}
}/*** 扩展抽象化角色*/
public class Mac extends OpratingSystem{public Mac(VideoFile videoFile) {super(videoFile);}@Overridepublic void play(String fileName) {videoFile.decode(fileName);}
}public class Test {public static void main(String[] args) {OpratingSystem system = new Mac(new AVIFile());system.play("飞升.avi");}}
五、外观模式
一种通过为多个复杂的子系统提供一个一致的接口,而使这些子系统更加容易被访问的模式。
该模式对外有一个统一的接口,外部应用程序不用关心内部子系统的具体的细节,
这样会大大降低应用程序的复杂度,提高程序的可维护性。
是迪米特法则的典型应用结构:外观角色:为多个子系统对外提供一个公共的接口子系统角色:实现系统的部分功能,外部可以通过外观角色访问它优点:1.降低了子系统和客户端之间的耦合度,使得子系统的变化不会影响调用它的类2.对客户屏蔽了子系统组件,减少了客户处理的对象的数目,并使得子系统使用起来更加容易缺点:不符合开闭原则,修改很麻烦使用场景:1.对分层结构系统构建时,使用外观模式定义子系统中每层的入口点可以简化子系统之间的依赖关系2.当一个复杂系统的子系统很多时,外观模式可以为系统设计一个简单的接口供外界访问3.当客户端与多个子系统之间存在很大的联系时,引入外观模式可将它们分离,从而提高子系统的独立性和可移植性
/*** 子系统角色*/
public class Light {public void on(){System.out.println("打开灯");}public void off(){System.out.println("关闭灯");}}/*** 子系统角色*/
public class TV {public void on(){System.out.println("打开电视");}public void off(){System.out.println("关闭电视");}}/*** 子系统角色*/
public class AirCondition {public void on(){System.out.println("打开空调");}public void off(){System.out.println("关闭空调");}}/*** 外观角色*/
public class SmartAppliancesFacade {private Light light;private TV tv;private AirCondition airCondition;public SmartAppliancesFacade() {light = new Light();tv = new TV();airCondition = new AirCondition();}public void say(String msg){if (msg.contains("打开")){this.on();} else if (msg.contains("关闭")) {this.off();} else {System.out.println("请说出正确指令");}}private void on(){light.on();airCondition.on();tv.on();}private void off(){light.off();airCondition.off();tv.off();}
}public class Test {public static void main(String[] args) {SmartAppliancesFacade smart = new SmartAppliancesFacade();smart.say("把他们打开");System.out.println("----------------");smart.say("把他们关闭");}}
六、组合模式
用于把一组相似的对象当作一个单一的对象。组合模式依据树形结构来组合对象,用来表示部分以及整体层次结构:抽象根节点:定义系统各层次对象的共有方法和属性,可以预先定义一些默认行为和属性树枝节点:定义树枝节点的行为,存储子节点,组合树枝节点和叶子节点形成一个树形结构叶子节点:叶子节点对象,其下再无分支,是系统层次遍历的最小单位分类:根据抽象构建类的定义形式,可以将组合模式分为透明组合模式和安全组合模式两种形式
1.透明组合模式:抽象根节点角色中声明了所有用于管理成员对象的方法,这样做的好处是确保所有的构件类都有相同的接口,是组合模式的标准形式。透明模式的缺点是不够安全,因为叶子对象和容器对象在本质上是有区别的,叶子对象不可能有下一个层次的对象,即不可能包含成员对象,因此为其提供某些方法是没有意义的,这在编译阶段不会出错,但是在运行阶段如果调用这些方法可能会出错。
2.安全组合模式:在抽象构件角色中没有声明任何用于管理成员对象的方法,而是在树枝节点类中声明这些方法。缺点是不够透明,因为叶子构件和容器构件具有不同的方法,且容器构件中那些用于管理成员对象的方法没有在抽象构件中定义,因此在客户端不能完全针对抽象编程,因此有区别的对待叶子构件和容器构建。优点:1.组合模式可以清楚地定义分层次的复杂对象,表示对象的全部或部分层次,他让客户端忽略了层次的差异,方便对整个层次结构进行控制2.客户端可以一致的使用一个组合结构或其中单个对象,不必关心处理的是单个对象还是整个组合,简化了客户端代码3.在组合模式中增加新的树枝节点和叶子节点都很方便,无须对现有类库进行任何修改4.组合模式为树形结构的面向对象实现提供了一种灵活的解决方案,通过叶子节点和树枝节点的递归组合,可以形成复杂的树形结构,但对树形结构的控制却非常简单使用场景:在出现树形结构的地方,比如:文件目录显示、多级目录呈现等
/*** 抽象根节点*/
public abstract class MenuComponent {public String name;public int level;//层级//添加子菜单public void add(MenuComponent menuComponent){throw new UnsupportedOperationException();//默认抛出异常 菜单项不能添加子菜单}//移除子菜单public void remove(MenuComponent menuComponent){throw new UnsupportedOperationException();//默认抛出异常 菜单项不能移除子菜单}//获取指定的子菜单public MenuComponent getChild(int index){throw new UnsupportedOperationException();//默认抛出异常 菜单项不能获取子菜单}//获取菜单或者菜单项public String getName(){return name;}//打印菜单名称(包含子菜单和子菜单项)public abstract void print();}/*** 树枝节点*/
public class Menu extends MenuComponent{//菜单可以有多个子菜单或者子菜单项private List<MenuComponent> list = new ArrayList<>();public Menu(String name,int level){this.name = name;this.level = level;}@Overridepublic void add(MenuComponent menuComponent) {list.add(menuComponent);}@Overridepublic void remove(MenuComponent menuComponent) {list.remove(menuComponent);}@Overridepublic MenuComponent getChild(int index) {return list.get(index);}@Overridepublic void print() {//打印菜单名称for (int i = 0;i < level;i++){System.out.print("-");}System.out.println(name + "->");//打印子菜单或者子菜单项名称for (MenuComponent menuComponent : list) {menuComponent.print();}}
}/*** 叶子节点*/
public class MenuItem extends MenuComponent{public MenuItem(String name,int level){this.name = name;this.level = level;}@Overridepublic void print() {//打印菜单项的名称for (int i = 0;i < level;i++){System.out.print("-");}System.out.println(name);}
}public class Test {public static void main(String[] args) {//创建菜单树MenuComponent menu1 = new Menu("菜单管理",2);menu1.add(new MenuItem("页面访问",3));menu1.add(new MenuItem("展开菜单",3));menu1.add(new MenuItem("编辑菜单",3));menu1.add(new MenuItem("删除菜单",3));menu1.add(new MenuItem("新增菜单",3));MenuComponent menu2 = new Menu("权限管理",2);menu2.add(new MenuItem("页面访问",3));menu2.add(new MenuItem("提交保存",3));MenuComponent menu3 = new Menu("角色管理",2);menu3.add(new MenuItem("页面访问",3));menu3.add(new MenuItem("新增角色",3));menu3.add(new MenuItem("修改角色",3));//创建1级菜单MenuComponent component = new Menu("系统管理",1);//将2级菜单添加到1级菜单中component.add(menu1);component.add(menu2);component.add(menu3);//打印菜单名称component.print();}}
七、享元模式
运用共享技术来有效地支持大量细粒度对象的复用。它通过共享已经存在的对象来大幅度减少需要创建的对象的数量、
避免大量相似对象的开销,从而提高系统的资源利用率状态:内部状态:不会随着环境的改变而改变的可共享的部分外部状态:指随环境改变而改变的不可以共享的部分。享元模式的实现要领就是区分应用中的两种状态结构:抽象享元角色:通常是一个接口或抽象类,在抽象享元类中声明了具体享元类公共的方法,这些方法可以向外界提供享元对象的内部数据(内部状态),同时也可以通过这些方法来设置外部数据(外部状态)具体享元角色:它实现了抽象享元类,称为享元对象;在具体享元类中为内部状态提供了存储空间。通常可以结合单例模式来设计具体享元类,为每一个具体享元类提供唯一的享元对象非享元角色:并不是所有的抽象享元类的子类都需要被共享,不能被共享的子类可设计为非共享具体享元类;当需要一个非共享具体享元类的对象时可以直接通过实例化创建享元工厂角色:负责创建和管理享元角色。当客户对象请求一个享元对象时,享元工厂检查系统中是否存在符合要求的享元对象,如果存在则提供给客户;如果不存在的话,则创建一个新的享元对象优点:极大减少了内存中相似或者相同对象的数量,节约系统资源,提高系统性能享元模式中的外部状态相对独立,且不影响内部状态缺点:为了使对象可以共享,需要将享元对象的部分状态外部化,分离内部状态和外部状态,使程序逻辑复杂使用场景:一个系统有大量相同或者相似的对象,造成内存的大量耗费对象的大部分状态都可以外部化,可以将这些外部状态传入对象中
/*** 抽象享元角色*/
public abstract class AbstractBox {//获取图形public abstract String getShape();//显示图形及颜色public void disPlay(String color){System.out.println("方块形状: " + getShape() + " , 颜色: " + color);}}/*** 具体享元角色*/
public class IBox extends AbstractBox{@Overridepublic String getShape() {return "I";}
}/*** 具体享元角色*/
public class JBox extends AbstractBox{@Overridepublic String getShape() {return "J";}
}/*** 具体享元角色*/
public class OBox extends AbstractBox{@Overridepublic String getShape() {return "O";}
}/*** 享元工厂角色* 设计为单例 采用饿汉式*/
public class BoxFactory {private Map<String,AbstractBox> map;private static BoxFactory factory = new BoxFactory();private BoxFactory(){map = new HashMap<>();map.put("I",new IBox());map.put("J",new JBox());map.put("O",new OBox());}public static BoxFactory getInstance(){return factory;}//根据名称获取图形对象public AbstractBox getShape(String name){if (!map.containsKey(name)){throw new NullPointerException("工厂中没有当前对象,请先创建该对象");}return map.get(name);}}public class Test {public static void main(String[] args) {AbstractBox box = BoxFactory.getInstance().getShape("I");box.disPlay("黑色");}}
相关文章:
设计模式-结构型模式
文章目录 一、代理模式1.静态代理2.JDK动态代理3.CGLib动态代理4.三种代理对比 二、适配器模式1.类适配器模式2.对象适配器模式 三、装饰者模式静态代理和装饰者的区别 四、桥接模式五、外观模式六、组合模式七、享元模式 结构性模式描述如何将类或对象按某种布局组成更大的结构…...
 
BUUCTF学习(7): 随便注,固网杯
1、介绍 2、解题 11;show tables;# select * from 1919810931114514 concat(sel,ect from 1919810931114514 ) PEREPARE y from sql; ECCUTE y; -1; sEt sql CONCAt(se,lect * from 1919810931114514;); prePare stmt from sql; EXECUTE stmt; # 结束...
 
【文末福利】巧用Chat GPT快速提升职场能力:数据分析与新媒体运营
欢迎关注博主 Mindtechnist 或加入【智能科技社区】一起学习和分享Linux、C、C、Python、Matlab,机器人运动控制、多机器人协作,智能优化算法,滤波估计、多传感器信息融合,机器学习,人工智能等相关领域的知识和技术。关…...
 
院内导航系统厂商分析
随着医疗技术的不断发展和医院规模的不断扩大,院内导航系统成为了现代化医院不可或缺的一部分。患者就医时,一个高效便捷的导航系统可以帮助他们快速找到目标科室,同时也能提高医院的整体运营效率。本文将推荐五家在院内导航市场具有竞争力的…...
 
MES系统作业调度
一、MES系统作业调度的概念和功能 作业调度是指在制造过程中,根据生产计划和实际情况,合理安排和调度各项任务和资源,以达到最佳的生产效率和资源利用率。MES系统作业调度功能涉及以下方面: 1. 任务计划与分配:MES系…...
 
C++入门-引用
C入门-引用 前置知识点:函数栈帧的复用前置知识点:类型转换时产生的临时变量1.含义2.代码形式3.引用的价值1.传参数传参效率测试补充:C与Java中引用的区别 2.引用做返回值(前置知识:栈帧复用)1.传值返回2.传引用返回传引用返回并用引用接收3.静态变量传引用返回4.引用做返回值真…...
问题:Qt中软件移植到笔记本中界面出现塌缩
这是由于软件之前运行的设备DPI较低,移植到笔记本中显示设备DPI较高,导致窗体显示进行了缩放。 解决方案,在main.cpp中加入以下代码: if(QT_VERSION>QT_VERSION_CHECK(5,6,0)) QCoreApplication::setAttribute(Qt::AA_EnableHi…...
NDK编译脚本:Android.mk or CMakeLists.txt
本文来自于:https://github.com/xufuji456/FFmpegAndroid/blob/master/doc/NDK_compile_shell.md 前言 Android NDK以前默认使用Android.mk与Application.mk进行构建,但是在Android Studio2.2之后推荐使用CMake进行编译。 CMake是跨平台编译工具&#…...
 
低代码提速应用开发
低代码介绍 低代码平台是指一种能够帮助企业快速交付业务应用的平台。自2000年以来,低代码市场一直充斥着40大大小小的各种玩家,比如国外的Appian、K2、Pega Systems、Salesforce和Ultimus,国内的H3 BPM。 2015年以后,这个市场更是…...
 
Hi3516DV500 SVP_NNN添加opencv库记录
默认没有带opencv库,但是实际项目中需要用到opencv库,因此添加一下此库; 1:编译opencv源码,这里具体可以参考 海思Hi3516移植opencv以及错误调试_海思hi3516摄像头开发-CSDN博客 2:在工程的根目录下新建…...
 
BIO实战、NIO编程与直接内存、零拷贝深入剖析
原生 JDK 网络编程 BIO BIO,意为 Blocking I/O,即阻塞的 I/O。 BIO 基本上就是我们上面所说的生活场景的朴素实现。在 BIO 中类 ServerSocket 负责绑定 IP 地址,启动监听端口,等待客户连接;客户端 Socket 类的实例…...
 
计网第六章(应用层)(四)(电子邮件)
电子邮件采用客户/服务器的方式。 1、三个构成 电子邮件系统的三个组成构件:用户代理、邮件服务器以及电子邮件所需的协议。 用户代理是用户与电子邮件系统的接口,又称为电子邮件客户端软件。 邮件服务器是电子邮件系统的基础设施。因特网上所有的服…...
Lua篇笔记
. 和 : 的区别 lua的面向对象 Lua数据类型 nil number bool table string userdata thread function Lua-字符串连接 C#与Lua交互过程及原理 Lua中的闭包 常见的一些Lua功能 热重载: function reload_module(module_name) local old_module _G[module_name] --取…...
 
一种更具破坏力的DDoS放大攻击新模式
近日,内容分发网络(CDN)运营商Akamai表示,一种使网站快速瘫痪的DDoS放大攻击新方法正在被不法分子所利用。这种方法是通过控制数量巨大的中间设备(middlebox,主要是指配置不当的服务器)…...
 
WordPress 常规设置页面调用媒体中心上传图片插入URL(新版可用)
首先,我们需要在主题或插件文件夹中创建一个 JavaScript 文件(如:media-uploader.js),该文件中包含如下代码。 /*** 媒体中心上传 js **/ jQuery(document).ready(function($){var mediaUploader;$(#upload_image_but…...
 
Elasticsearch实现检索词自动补全(检索词补全,自动纠错,拼音补全,繁简转换) 包含demo
Elasticsearch实现检索词自动补全 自动补全定义映射字段建立索引测试自动补全 自动纠错查询语句查询结果 拼音补全与繁简转换安装 elasticsearch-analysis-pinyin 插件定义索引与映射建立拼音自动补全索引测试拼音自动补全测试繁简转换自动补全 代码实现demo结构demo获取 自动补…...
 
LaunchView/启动页 的实现
1. 创建启动画板,LaunchScreen.storyboard 添加组件如图: 2. 项目中设置只支持竖屏,添加启动画板,如图: 3. 创建启动画面动画视图,LaunchView.swift import SwiftUI/// 启动视图 struct LaunchView: View {/// 字符串转换为字符串…...
 
windows安装npm教程
在安装和使用NPM之前,我们需要先了解一下,NPM 是什么,能干啥? 一、NPM介绍 NPM(Node Package Manager)是一个用于管理和共享JavaScript代码包的工具。它是Node.js生态系统的一部分,广泛用于构…...
 
网络端口验证
网络端口连通性验证 1、背景2、目标3、环境4、部署4.1、准备工作4.2、安装4.3、场景测试 1、背景 在日常运维过程中经常会遇到以下两种场景: 1、程序业务端口的开具及验证 2、业务程序访问异常网络排障 2、目标 1、验证端口的正确开具 2、网络策略的连通性 3、环…...
 
MongoDB 索引和常用命令
一、基本常用命令 1.1 案例需求 存放文章评论的数据存放到 MongoDB 中,数据结构参考如下,其中数据库为 articledb, 专栏文章评论 comment 字段名称 字段含义 字段类型 备注 _id ID ObjectId或String Mongo的主键的字段 articlei…...
 
如何在看板中体现优先级变化
在看板中有效体现优先级变化的关键措施包括:采用颜色或标签标识优先级、设置任务排序规则、使用独立的优先级列或泳道、结合自动化规则同步优先级变化、建立定期的优先级审查流程。其中,设置任务排序规则尤其重要,因为它让看板视觉上直观地体…...
Leetcode 3577. Count the Number of Computer Unlocking Permutations
Leetcode 3577. Count the Number of Computer Unlocking Permutations 1. 解题思路2. 代码实现 题目链接:3577. Count the Number of Computer Unlocking Permutations 1. 解题思路 这一题其实就是一个脑筋急转弯,要想要能够将所有的电脑解锁&#x…...
 
学习STC51单片机31(芯片为STC89C52RCRC)OLED显示屏1
每日一言 生活的美好,总是藏在那些你咬牙坚持的日子里。 硬件:OLED 以后要用到OLED的时候找到这个文件 OLED的设备地址 SSD1306"SSD" 是品牌缩写,"1306" 是产品编号。 驱动 OLED 屏幕的 IIC 总线数据传输格式 示意图 …...
 
WordPress插件:AI多语言写作与智能配图、免费AI模型、SEO文章生成
厌倦手动写WordPress文章?AI自动生成,效率提升10倍! 支持多语言、自动配图、定时发布,让内容创作更轻松! AI内容生成 → 不想每天写文章?AI一键生成高质量内容!多语言支持 → 跨境电商必备&am…...
 
ios苹果系统,js 滑动屏幕、锚定无效
现象:window.addEventListener监听touch无效,划不动屏幕,但是代码逻辑都有执行到。 scrollIntoView也无效。 原因:这是因为 iOS 的触摸事件处理机制和 touch-action: none 的设置有关。ios有太多得交互动作,从而会影响…...
《C++ 模板》
目录 函数模板 类模板 非类型模板参数 模板特化 函数模板特化 类模板的特化 模板,就像一个模具,里面可以将不同类型的材料做成一个形状,其分为函数模板和类模板。 函数模板 函数模板可以简化函数重载的代码。格式:templa…...
 
安宝特案例丨Vuzix AR智能眼镜集成专业软件,助力卢森堡医院药房转型,赢得辉瑞创新奖
在Vuzix M400 AR智能眼镜的助力下,卢森堡罗伯特舒曼医院(the Robert Schuman Hospitals, HRS)凭借在无菌制剂生产流程中引入增强现实技术(AR)创新项目,荣获了2024年6月7日由卢森堡医院药剂师协会࿰…...
 
推荐 github 项目:GeminiImageApp(图片生成方向,可以做一定的素材)
推荐 github 项目:GeminiImageApp(图片生成方向,可以做一定的素材) 这个项目能干嘛? 使用 gemini 2.0 的 api 和 google 其他的 api 来做衍生处理 简化和优化了文生图和图生图的行为(我的最主要) 并且有一些目标检测和切割(我用不到) 视频和 imagefx 因为没 a…...
 
使用Spring AI和MCP协议构建图片搜索服务
目录 使用Spring AI和MCP协议构建图片搜索服务 引言 技术栈概览 项目架构设计 架构图 服务端开发 1. 创建Spring Boot项目 2. 实现图片搜索工具 3. 配置传输模式 Stdio模式(本地调用) SSE模式(远程调用) 4. 注册工具提…...
 
华为OD机考-机房布局
import java.util.*;public class DemoTest5 {public static void main(String[] args) {Scanner in new Scanner(System.in);// 注意 hasNext 和 hasNextLine 的区别while (in.hasNextLine()) { // 注意 while 处理多个 caseSystem.out.println(solve(in.nextLine()));}}priv…...
