一、普通工厂模式,就是建立一个工厂类,对实现了同一接口的一些类进行实例的创建。首先看下关系图:
举例如下:(我们举一个发送邮件和短信的例子)
首先,创建二者的共同接口:
- public interface Sender {
- public void Send();
- }
其次,创建实现类:
- public class MailSender implements Sender {
- @Override
- public void Send() {
- System.out.println("this is mailsender!");
- }
- }
- public class SmsSender implements Sender {
- @Override
- public void Send() {
- System.out.println("this is sms sender!");
- }
- }
最后,建工厂类:
- public class SendFactory {
- public Sender produce(String type) {
- if ("mail".equals(type)) {
- return new MailSender();
- } else if ("sms".equals(type)) {
- return new SmsSender();
- } else {
- System.out.println("请输入正确的类型!");
- return null;
- }
- }
- }
我们来测试下:
- public class FactoryTest {
- public static void main(String[] args) {
- SendFactory factory = new SendFactory();
- Sender sender = factory.produce("sms");
- sender.Send();
- }
- }
原文链接:https://blog.csdn.net/llussize/article/details/80276627
二、适配器模式
适配器模式是一种结构型设计模式。适配器模式的思想是:把一个类的接口变换成客户端所期待的另一种接口,从而使原本因接口不匹配而无法在一起工作的两个类能够在一起工作。
用电器来打个比喻:有一个电器的插头是三脚的,而现有的插座是两孔的,要使插头插上插座,我们需要一个插头转换器,这个转换器即是适配器。
适配器模式涉及3个角色:
源(Adaptee):需要被适配的对象或类型,相当于插头。
适配器(Adapter):连接目标和源的中间对象,相当于插头转换器。
目标(Target):期待得到的目标,相当于插座。
适配器模式包括3种形式:类适配器模式、对象适配器模式、接口适配器模式(或又称作缺省适配器模式)。
类适配器模式
从下面的结构图可以看出,Adaptee类并没有method2()方法,而客户端则期待这个方法。为使客户端能够使用Adaptee类,我们把Adaptee与Target衔接起来。Adapter与Adaptee是继承关系,这决定了这是一个类适配器模式。
代码实现:
源:
public class Adaptee {
public void method1(){
System.out.println("method 1");
}
}
目标:
public interface Target {
void method1();
void method2();
}
适配器:
public class Adapter extends Adaptee implements Target {
@Override
public void method2() {
System.out.println("method 2");
}
}
// 测试
class AdapterTest {
public static void main(String[] args) {
Adapter adapter = new Adapter();
adapter.method1();
adapter.method2();
}
}
运行结果:
method 1
method 2
对象适配器模式
对象适配器模式是另外6种结构型设计模式的起源。
从下面的结构图可以看出,Adaptee类并没有method2()方法,而客户端则期待这个方法。与类适配器模式一样,为使客户端能够使用Adaptee类,我们把Adaptee与Target衔接起来。但这里我们不继承Adaptee,而是把Adaptee封装进Adapter里。这里Adaptee与Adapter是组合关系。
代码实现:
Target和Adaptee和上面的类适配器一样,不再贴出。
适配器:
public class Adapter implements Target {
private Adaptee adaptee;
public Adapter(Adaptee adaptee) {
this.adaptee = adaptee;
}
@Override
public void method1() {
adaptee.method1();
}
@Override
public void method2() {
System.out.println("method 2");
}
}
class AdapterTest {
public static void main(String[] args) {
Adapter adapter = new Adapter(new Adaptee());
adapter.method1();
adapter.method2();
}
}
运行结果:
method 1
method 2
类适配器与对象适配器的区别
类适配器使用的是继承的方式,直接继承了Adaptee,所以无法对Adaptee的子类进行适配。
对象适配器使用的是组合的方式,·所以Adaptee及其子孙类都可以被适配。另外,对象适配器对于增加一些新行为非常方便,而且新增加的行为同时适用于所有的源。
基于组合/聚合优于继承的原则,使用对象适配器是更好的选择。但具体问题应该具体分析,某些情况可能使用类适配器会适合,最适合的才是最好的。
接口适配器模式(缺省适配模式)
接口适配器模式(缺省适配模式)的思想是,为一个接口提供缺省实现,这样子类可以从这个缺省实现进行扩展,而不必从原有接口进行扩展。
这里提供一个例子。java.awt.KeyListener是一个键盘监听器接口,我们把这个接口的实现类对象注册进容器后,这个容器就会对键盘行为进行监听,像这样:
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.addKeyListener(new KeyListener() {
@Override
public void keyTyped(KeyEvent e) {}
@Override
public void keyPressed(KeyEvent e) {
System.out.println("hey geek!");
}
@Override
public void keyReleased(KeyEvent e) {
}
});
}
可以看到其实我们只使用到其中一个方法,但必须要把接口中所有方法都实现一遍,如果接口里方法非常多,那岂不是非常麻烦。于是我们引入一个默认适配器,让适配器把接口里的方法都实现一遍,使用时继承这个适配器,把需要的方法实现一遍就好了。JAVA里也为java.awt.KeyListener提供了这样一个适配器:java.awt.KeyAdapter。我们使用这个适配器来改改上面的代码:
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.addKeyListener(new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
System.out.println("fxcku!");
}
});
}
这样不必再把每个方法都实现一遍,代码看起来简洁多了。在任何时候,如果不准备实现一个接口里的所有方法时,就可以使用“缺省适配模式”制造一个抽象类,实现所有方法,这样,从这个抽象类再继承下去的子类就不必实现所有的方法,只要重写需要的方法就可以了。
原文链接:https://blog.csdn.net/mrkohaku/article/details/79087688
三、装饰器模式
定义
装饰器模式又名包装(Wrapper)模式。装饰器模式以对客户端透明的方式拓展对象的功能,是继承关系的一种替代方案。
类图
角色
抽象构件(Component)角色:给出一个抽象接口,已规范准备接收附加责任的对象。
具体构件(ConcreteComponent)角色:定义一个将要接收附加责任的类
装饰(Decorator)角色:持有一个构件(Component)对象的实例,并定义一个与抽象构件接口一致的接口。
具体装饰(ConcreteDecorator)角色:负责给构件对象“贴上”附加的责任。
示例
组件接口
public interface Component {
public String getName();
}
具体组件实现类A
public class ComponentImplA implements Component {
private String name;
public ComponentImplA(String name){
this.name = name;
}
@Override
public String getName() {
// TODO Auto-generated method stub
return name;
}
}
具体组件实现类B
public class ComponentImplB implements Component {
private String name;
public ComponentImplB(String name){
this.name = name;
}
@Override
public String getName() {
// TODO Auto-generated method stub
return name;
}
}
装饰器实现组件接口
public abstract class Decorator implements Component {
protected Component component;
public Decorator(Component component){
this.component = component;
}
@Override
public String getName() {
return component.getName();
}
}
具体的装饰器A,这里新增一个方法appendName;
public class DecoratorImplA extends Decorator {
public DecoratorImplA(Component component) {
super(component);
}
//增加额外的操作
public void appendName(String name){
System.out.println(this.component.getName()+name);
}
}
具体的装饰器B,这里重写原来的getName方法;
public class DecoratorImplB extends Decorator {
public DecoratorImplB(Component component) {
super(component);
}
@Override
public String getName() {
return super.getName()+"|append2";
}
}
测试类
public class DecoratorTest {
public static void main(String[] args) {
Component componentImplA = new ComponentImplA("componentA");
DecoratorImplA decoratorImplA = new DecoratorImplA(componentImplA);
decoratorImplA.appendName("|append1");
System.out.println(new DecoratorImplB(componentImplA).getName());;
}
}
分析
装饰器相较于继承来说,可以在运行时保持原来接口不变的情况下动态的给原对象增加新功能,而继承是静态的;
跟适配器比较:
理想的装饰器模式的装饰者接口跟组件接口应该保持一模一样,这样每次传入一个组件进去,都只对组件接口的各个方法增加功能,返回依旧由组件接口接收,这样调用同一个对象的同一个方法会有不同的实现,调用者完全不必要知道具体调用的是哪个对象,这个也称之为透明装饰器;
如果装饰者接口跟组件接口不一致,装饰者接口对组件接口方法进行了扩增,也是增加了新功能,但是如果还是用原组件接口来接收的话,新增的方法需要向下转型才能使用,这就违背了装饰器的初衷,但是组件接口中的方法仍然可以正常使用,因此这种可称之为半透明装饰器;
如果只是需要将原来对象或者类转换成另外一个我们需要的对象接口,不增加新功能,这种就是适配器;
简单的来理解的话,装饰器用来新加功能,适配器只是用来转换功能;
原文链接:https://blog.csdn.net/qq_30325833/article/details/90288832