设计模式2(结构型模式)

设计模式2(结构型模式)

结构型模式
这类模式介绍如何将对象和类组装成较大的结构,并同时保持结构的灵活和高效。

unknown_filename.4|209x0

桥接模式

桥接模式的主要作用就是通过将抽象部分与实现部分分离,把多种可匹配的使用进行组合。说白了核心实现也就是在 A 类中含有 B 类接口,通过构造函数传递 B 类的实现,这个 B 类就是设计的桥。

unknown_filename.5|600

从上面的ifelse方式实现来看,这是两种不同类型的相互组合。那么就可以把支付方式和支付模式进行分离通过抽象类依赖实现类的方式进行桥接,通过这样的拆分后支付与模式其实是可以单独使用的,当需要组合时候只需要把模式传递给支付即可。
桥接模式的关键是选择的桥接点拆分,是否可以找到这样类似的相互组合,如果没有就不必要非得使用桥接模式。

1
2
3
4
5
6
7
8
9
10
11
12
@Test
public void test_pay() {

    System.out.println("\r\n模拟测试场景;微信支付、人脸方式。");
    Pay wxPay = new WxPay(new PayFaceMode());
    wxPay.transfer("weixin_1092033111", "100000109893", new BigDecimal(100));

    System.out.println("\r\n模拟测试场景;支付宝支付、指纹方式。");
    Pay zfbPay = new ZfbPay(new PayFingerprintMode());
    zfbPay.transfer("jlu19dlxo111","100000109894",new BigDecimal(100));

}

unknown_filename.6

外观模式(门面模式)

(context)
外观模式的主要目的在于 让外部减少与 子系统内部的多个模块的交互,从而让外部能够更简单得使用子系统。它负责把客户端的请求转发给子系统内部的各个模块进行处理。
简单点理解就是,通过创建一个统一的类,用来包装子系统中一个或多个复杂的类,客户端可以通过调用外观类的方法来调用内部子系统中所有方法。
unknown_filename.2|639x0

facade和module是合成关系

  • 由于 Facade 类封装了各个模块交互的过程,如果今后内部模块调用关系发生了变化,只需要修改 Facade 实现就可以了。
  • Facade实现是可以被多个客户端调用的
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
public class ModuleA {
    public void testFuncA() {
        System.out.println("This is Function From 改变了");
    }
}
public class ModuleB {
    public void testFuncB() {
        System.out.println("This is Function From ModuleB");
    }
}
public class Facade {
    private ModuleA moduleA = null;
    private ModuleB moduleB = null;
    private ModuleC moduleC = null;
    private static Facade mFacade = null;
    private Facade(){
        moduleA = new ModuleA();
        moduleB = new ModuleB();
        moduleC = new ModuleC();
    }   
    public static Facade getInstance() {
        if(mFacade == null) {
            mFacade = new Facade();
        }
        return mFacade;
    }

    public void testOperation() {
        moduleA.testFuncA();
        moduleB.testFuncB();
        moduleC.testFuncC();
    }
}
public class Client {
    public static void main(String arg[]) {
        Facade.getInstance().testOperation();
    }
}

装饰模式

(Collections 工具类、I/O、context)

对一组对象的功能进行增强时,就可以使用该模式进行问题的解决,是一种对象结构型模式。装饰和继承都能实现一样的特点:进行功能的扩展增强。 但是只为提高功能,进行的继承,会导致继承体系越来越臃肿,不够灵活。

与继承关系相比,关联关系的主要优势在于不会破坏类的封装性,而且继承是一种耦合度较大的静态关系,无法在程序运行时动态扩展。在软件开发阶段,关联关系虽然不会比继承关系减少编码量,但是到了软件维护阶段,由于关联关系使系统耦合性不高,因此使得系统更加容易维护。当然,关联关系的缺点是比继承关系要创建更多的对象。

使用场景
(1)在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责。
(2)当不能采用继承的方式对系统进行扩展,或者采用继承不利于系统扩展和维护时可以使用装饰模式

  • Component: 抽象构件
  • ConcreteComponent: 具体构件
  • Decorator: 抽象装饰类
  • ConcreteDecorator: 具体装饰类

ConcreteDecorator和Decorator都是继承****component
unknown_filename.3|787x0

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
public class DecoratorExample {

    /**
     * 抽象构件
     */
    interface Component {
        void operation();
    }

    /**
     * 具体构件
     */
    class ConcreteComponent implements Component {

        @Override
        public void operation() {
            System.out.println("from ConcreteComponent");
        }
    }

    /**
     * 抽象装饰类
     */
    class Decorator implements Component {
        private Component component;  //维持一个对抽象构件对象的引用

        public Decorator(Component component) { //注入一个抽象构件类型的对象
            this.component = component;
        }

        @Override
        public void operation() {
            component.operation();  //调用原有业务方法
        }
    }

    /**
     * 具体装饰类
     */
    class ConcreteDecoratorA extends Decorator {
        public ConcreteDecoratorA(Component component) {
            super(component);
        }

        @Override
        public void operation() {
            //俩方法位置随便替换
            super.operation();  //调用原有业务方法
            addedBehavior();  //调用新增业务方法
        }

        //新增业务方法
        public void addedBehavior() {
            System.out.println("from ConcreteDecoratorA");
        }
    }

    class ConcreteDecoratorB extends Decorator {
        public ConcreteDecoratorB(Component component) {
            super(component);
        }

        @Override
        public void operation() {
            super.operation();  
            addedBehavior();  
        }

        //新增业务方法
        public void addedBehavior() {
            System.out.println("from ConcreteDecoratorB");
        }
    }

    public static void main(String[] args) {
        //为了方便看就写到一个类里了,理解意思即可
//        ConcreteComponent concreteComponent = new ConcreteComponent();
//        Decorator decorator = new Decorator(concreteComponent);
//        ConcreteDecoratorA concreteDecoratorA = new ConcreteDecoratorA(decorator);
//        ConcreteDecoratorB concreteDecoratorB = new ConcreteDecoratorB(concreteDecoratorA);
//        concreteDecoratorB.operation();

    }
}

适配器(adapter)模式

把一个类的接口变换成客户端所期待的另一种接口,从而使原本因接口不匹配而无法在一起使用的类能够一起工作。
适配器就是一种适配中间件,它存在于不匹配的二者之间,用于连接二者,将不匹配变得匹配,简单点理解就是平常所见的转接头,转换器之类的存在。

类适配器

unknown_filename.8|196x0

原理:通过继承来实现适配器功能。

类适配器使用对象继承的方式,是静态的定义方式
对于类适配器,适配器可以重定义Adaptee的部分行为,使Adaptee有了sampleOperation2()
对于类适配器,仅仅引入了一个对象,并不需要额外的引用来间接得 到 Adaptee
对于类适配器,由于适配器直接继承了Adaptee,使得适配器不能和 Adaptee的子类一起工作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public interface Target {

    void sampleOperation1();

    void sampleOperation2();
}

public class Adaptee {

    public void sampleOperation1() {
        System.out.println("sampleOperation1");
    }
}

public class Adapter extends Adaptee implements Target {
    @Override
    public void sampleOperation2() {
        System.out.println("sampleOperation2");
    }
}

public class MyClass {
    public static void main(String[] args) {
        Adapter adapter = new Adapter();
        adapter.sampleOperation1();
        adapter.sampleOperation2();
    }
}

对象适配器

与类的适配器模式一样,对象的适配器模式把被适配的类的API转换成为目标类的API,与类的适配器模式不同的是,对象的适配器模式不是使用继承关系连接到Adaptee类,而是使用委派关系连接到Adaptee类。

对象适配器使用对象组合的方式,是动态组合的方式

对于对象适配器,一个适配器(adaptee)可以把多种不同的源适配到同一个目标

对于对象适配器,要重定义Adaptee的行为比较困难

对于对象适配器,需要额外的引用来间接得到Adaptee。

unknown_filename.7|332x0

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
public interface Target {

    void sampleOperation1();

    void sampleOperation2();
}

public class Adaptee {
    public void sampleOperation1() {
        System.out.println("sampleOperation1");
    }
}

public class Adapter implements Target {

    private Adaptee mAdaptee;

    public Adapter(Adaptee adaptee) {
        mAdaptee = adaptee;
    }

    @Override
    public void sampleOperation1() {
        mAdaptee.sampleOperation1();
    }

    @Override
    public void sampleOperation2() {
        System.out.println("sampleOperation2");
    }

}

public class MyClass {

    public static void main(String[] args) {
        Adapter adapter =new Adapter(new Adaptee());
        adapter.sampleOperation1();
        adapter.sampleOperation2();
    }
}


适配器模式不并是那种会让架构变得更合理的模式,更多的时候它只是充当救火队员的角色,帮助解决由于前期架构设计不合理导致的接口不匹配的问题。更好的做法是在设计的时候就尽量把以后可能出现的情况多考虑一些,例如这个例子,ServerFirst没有PlayerCount 方法,可以写一个适配器对象,实现PlayerCount 接口,新接口中调用旧接口的方法

Java设计模式透析之 —— 适配器(Adapter)_adpter注解 java-CSDN博客

代理模式

通过代理对象访问目标对象.这样做的好处是:可以在目标对象实现的基础上,扩展目标对象的功能。或者交给另一个类去做事情
代理对象拦截真正对象的方法调用,在真正的对象调用前/后实现自己的逻辑调用
这里使用到编程中的一个思想:不要随意去修改别人已经写好的代码或者方法,如果需改修改,可以通过代理的方式来扩展该方法。

动态代理的用途与装饰模式很相似,就是为了对某个对象进行增强,还有交给另一个去做事。所有使用装饰者模式的案例都可以使用动态代理来替换。

unknown_filename|708x0

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
/**
* subject(抽象主题角色):
* 真实主题与代理主题的共同接口。
*/
interface Subject {
    void sellBook();
}

/**
* ReaISubject(真实主题角色):
* 定义了代理角色所代表的真实对象。
*/
public class RealSubject implements Subject {
    @Override
    public void sellBook() {
        System.out.println("出版社卖书");
    }
}

/**
* Proxy(代理主题角色):
* 含有对真实主题角色的引用,代理角色通常在将客
* 户端调用传递给真实主题对象之前或者之后执行某些
* 操作,而不是单纯返回真实的对象。
*/
public class ProxySubject implements Subject {

    private RealSubject realSubject;

    @Override
    public void sellBook() {
        if (realSubject == null) {
            realSubject = new RealSubject();
        }
        sale();
        realSubject.sellBook();
        give();
    }

    public void sale() {
        System.out.println("打折");
    }

    public void give() {
        System.out.println("送优惠券");
    }
}

public class Main {
    public static void main(String[] args) {
        //静态代理(我们自己静态定义的代理类)
        ProxySubject proxySubject = new ProxySubject();
        proxySubject.sellBook();

        //动态代理(通过程序动态生成代理类,该代理类不是我们自己定义的。而是由程序自动生成)
        RealSubject realSubject = new RealSubject();
        MyHandler myHandler = new MyHandler();
        myHandler.setProxySubject(realSubject);
        Subject subject = (Subject) Proxy.newProxyInstance(realSubject.getClass().getClassLoader(),
                realSubject.getClass().getInterfaces(), myHandler);
        subject.sellBook();

    }
}

public class MyHandler implements InvocationHandler {
    private RealSubject realSubject;

    public void setProxySubject(RealSubject realSubject) {
        this.realSubject = realSubject;
    }

    /**
     * @param proxy   指代我们所代理的那个真实对象
     * @param method   指代的是我们所要调用真实对象的某个方法的Method对象
     * @param args    指代的是调用真实对象某个方法时接受的参数
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        sale();
        proxy = method.invoke(realSubject, args);
        give();
        return proxy;
    }

    public void sale() {
        System.out.println("打折");
    }

    public void give() {
        System.out.println("送优惠券");
    }
}

享元模式

(string)

享元的目的是为了减少不必要的内存消耗,将多个对象的访问集中起来,不必为每个访问者创建一个单独的对象,以此来降低内存的消耗。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
public class FlyWeight {

    static class MyString {
        private String myChar;

        public MyString(String myChar) {
            this.myChar = myChar;
        }

        public void display() {
            System.out.println(myChar);
        }
    }

    static class MyCharacterFactory {

        private Map<String, MyString> pool;

        public MyCharacterFatory() {
            pool = new HashMap<>();
        }

        public MyString getMyCharacte(String strig) {
            MyString myString = pool.get(strig);
            if (myString == null) {
                myString = new MyString(strig);
                pool.put(strig, myString);
            }
            return myString;
        }
    }

    public static void main(String[] args) {
        MyCharacterFactory myCharacterFactory = new MyCharacterFactory();
        MyString a = myCharacterFactory.getMyCharacte("a");
        MyString b = myCharacterFactory.getMyCharacte("b");
        MyString a1 = myCharacterFactory.getMyCharacte("a");
        MyString d = myCharacterFactory.getMyCharacte("d");

        if (a == a1) {
            System.out.println("true");
        }

    }
}

组合

将对象组合成树形结构以表示”部分-整体”的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。
客户程序可以向处理简单元素一样来处理复杂元素,从而使得客户程序与复杂元素的内部结构解耦。

Component : 组合中的对象声明接口,在适当的情况下,实现所有类共有接口的默认行为。

Leaf : 表示叶节点对象。叶子节点没有子节点。

Composite : 定义枝节点行为,用来存储子部件,在 Component 接口中实现与子部件相关的操作。例如 Add 和 Remove

unknown_filename.1|782x0

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
public abstract class File {

    private String name;

    public File(String name) {
        this.name = name;
    }

    //操作方法
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public abstract void watch();

    //组合方法
    public void add(File file) {
        throw new UnsupportedOperationException();
    }

    public void remove(File file) {
        throw new UnsupportedOperationException();
    }

    public File getChild(int position) {
        throw new UnsupportedOperationException();
    }
}

public class Folder extends File {

    private List<File> mFileList;

    public Folder(String name) {
        super(name);
        mFileList = new ArrayList<>();
    }

    @Override
    public void watch() {
        StringBuffer fileName = new StringBuffer();
        for (File file : mFileList) {
            fileName.append(file.getName() + ";");
        }

          System.out.println("组合模式:这是一个叫" + getName() + "文件夹,包含" + mFileList.size() + "个文件,分别是:" + fileName);

    }

    @Override
    public void add(File file) {
        mFileList.add(file);
    }

    @Override
    public void remove(File file) {
        mFileList.remove(file);
    }

    @Override
    public File getChild(int position) {
        return mFileList.get(position);
    }
}

public class TextFile extends File {

    public TextFile(String name) {
        super(name);
    }

    @Override
    public void watch() {
        System.out.println("组合模式:这是一个叫" + getName() + "文本文件");
    }
}

public class testComposite {
    public static void main(String[] strings) {
        TextFile textFileA = new TextFile("a.txt");
        TextFile textFileB = new TextFile("b.txt");
        TextFile textFileC = new TextFile("c.txt");

        textFileA.watch();
    //  textFileA.add(textFileB);//调用会抛我们在抽象接口中写的异常

        Folder folder = new Folder("学习资料");
        folder.add(textFileA);
        folder.add(textFileB);
        folder.add(textFileC);
        folder.watch();
        folder.getChild(1).watch();

        Folder folder1 = new Folder("个人资料");
        folder1.add(textFileA);
        folder1.add(textFileB);
        //可以随便组合
        Folder folder2 = new Folder("D盘资料");
        folder2.add(folder);
        folder2.add(folder1);

    }

}

委托模式(没有这种模式)

委托(Delegate)是 Kotlin 的一种语言特性,用于更加优雅地实现代理模式。

委托模式是软件设计模式中的一项基本技巧。在委托模式中,有两个对象参与处理同一个请求,接受请求的对象将请求委托给另一个对象来处理。委托模式是一项基本技巧,许多其他的模式,如状态模式、策略模式、访问者模式本质上是在更特殊的场合采用了委托模式。委托模式使得我们可以用聚合来替代继承,它还使我们可以模拟 mixin。

简单的 Java例子

在这个例子里,类模拟打印机Printer拥有针式打印机RealPrinter的实例,Printer拥有的方法print()将处理转交给RealPrinter的方法print()。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class RealPrinter { // the "delegate"
     void print() {
       System.out.print("something");
     }
}
class Printer { // the "delegator"
     RealPrinter p = new RealPrinter(); // create the delegate
     void print() {
       p.print(); // delegation
     }
}
public class Main {
     // to the outside world it looks like Printer actually prints.
     public static void main(String[] args) {
         Printer printer = new Printer();
         printer.print();
     }
}

复杂的 Java 例子

通过使用接口,委托可以做到类型安全并且更加灵活。在这个例子里,类别C可以委托类别A或类别B,类别C拥有方法使自己可以在类别A或类别B间选择。因为类别A或类别B必须实现接口I规定的方法,所以在这里委托是类型安全的。这个例子显示出委托的缺点是需要更多的代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
interface I {
     void f();
     void g();
}
class A implements I {
     public void f() { System.out.println("A: doing f()"); }
     public void g() { System.out.println("A: doing g()"); }
}
class B implements I {
     public void f() { System.out.println("B: doing f()"); }
     public void g() { System.out.println("B: doing g()"); }
}
class C implements I {
     // delegation
     I i = new A();
     public void f() { i.f(); }
     public void g() { i.g(); }
     // normal attributes
     public void toA() { i = new A(); }
     public void toB() { i = new B(); }
}
public class Main {
     public static void main(String[] args) {
         C c = new C();
         c.f();     // output: A: doing f()
         c.g();     // output: A: doing g()
         c.toB();
         c.f();     // output: B: doing f()
         c.g();     // output: B: doing g()
     }
}

设计模式2(结构型模式)
http://peiniwan.github.io/2024/04/9f3ef099adb5.html
作者
六月的雨
发布于
2024年4月6日
许可协议