Java设计模式详解:创建型、结构型、行为型模式分类及六大设计原则解析
Java设计模式一般分为三类:

创建模式(5种):工厂方法模式、抽象工厂模式、单例模式、构建器模式、
原型模式。
结构模式(7种):适配器模式、装饰器模式、代理模式、外观模式、桥接模式
模式、组合模式、享元模式。
行为模式(11种):策略模式、模板方法模式、观察者模式、迭代子模式、
责任链模型、命令模型、备忘录模型、状态模型、访客模型、中介模型、解决方案
口译员模式。
设计模式遵循 6 个原则:
1. 开闭原理
对扩展开放,对修改关闭。
2.里氏替换原理
只有当派生类能够替代基类并且不影响软件单元的功能时,基类才能真正被实现。
被重用,派生类还可以在基类的基础上添加新的行为。
3. 依赖倒置原理
这是开闭原则的基础。接口编程依赖于抽象而不是具体。
4. 接口隔离原则
使用多个隔离的接口来减少耦合。
5.德米特原理(最不为人知的原理)
一个实体应该尽可能少地与其他实体交互,以便系统功能模块之间尽可能少地交互。
到独立。
6. 复合复用原则
原则是尽可能使用组合/聚合,而不是继承。继承实际上破坏了类
由于封装,超类方法可以被子类修改。
1、单例模式:保证某个类只有一个实例,自己实例化,并将其提供给整个系统。
实例。
(一)懒人风格
1 公共类单例{
23 /* 持有一个私有静态实例以防止它被引用。这里赋值为null,实现延迟加法。
加载*/
4 私有静态单例实例= null;
6 /* 私有构造函数,防止被实例化*/
7 私有单例() {
8}
10 /* 1:惰性风格,静态工程方法,创建实例*/
11 公共静态单例 getInstance() {
12 if (实例== null) {
13 实例 = new Singleton();
14}
15 返回实例;
16}
17}
(二)饿汉式
1 公共类单例{
3 /* 持有一个私有静态实例,防止被引用*/
4 私有静态Singleton实例 = new Singleton();
6 /* 私有构造函数以防止实例化*/
7 私有单例() {
8}
10 /* 1:惰性风格,静态工程方法,创建实例*/
11 公共静态单例 getInstance() {
12 返回实例;
13}
14}
使用场景:
1、需要生成唯一序列号的环境; 2. 整个项目的共享访问点或共享数据,例如网页上的计数
不用将每次刷新记录到数据库中,而是使用单例模式来保留计数器值并
确保它是线程安全的;
3、创建对象需要太多资源,比如访问IO、数据库资源;
4、在需要定义大量静态常量和静态方法(如工具类)的环境中,可以使用单例模式
(当然也可以直接声明为static)。
2、工厂模式:定义一个用于创建对象的接口,让子类决定实例化哪个类。工厂
方法允许将类的实例化推迟到其子类。
界面
1 公共接口 Fruit {
2 公共无效打印();
3}
4 2个实现类
6 公共类苹果实现Fruit{
8 @覆盖
9 公共无效打印(){
10 System.out.println("我是苹果");
11}
12
13}
14 公共类Orange实现Fruit{
15
16 @覆盖
17 公共无效打印(){
18 System.out.println("我是一个橙子");
19}
20
21 }
工厂类 1 公共类 FruitFactory {
2 公共水果产品(字符串类型){
3 if(type.equals("苹果")){
4 返回new Apple();
5 }else if(type.equals("orange")){
6 返回新Orange();
7}其他{
8 System.out.println("请输入正确的类型!");
9 返回空值;
10}
11}
12}
使用场景:jdbc连接数据库、硬件访问、减少对象创建和销毁
3、抽象工厂模式:提供一个接口,用于创建一组相关或相互依赖的对象,而无需指定
定义他们的具体类。
与工厂模式相比,我们可以添加新的产品类别(只需要实现产品接口),并且只需要添加新的
通过添加工厂类,客户端可以轻松调用新产品的代码。
1 个接口食物{}
2 A 类实现食物{}
3 B 类实现 food{}
4 接口 Produce{ food get();}
5类FactoryForA实现了product{
6 @覆盖
7 公共食物 get() {
8 返回新的A();
9}
10}
11类FactoryForB实现了product{
12 @覆盖
13 公共食物 get() {
14 返回新B();
15}
16 }17 公共类 AbstractFactory {
18 public void ClientCode(字符串名称){
19 食物 x= new FactoryForA().get();
20 x = new FactoryForB().get();
21 }
22 }
使用场景:一族对象(或一组不相关的对象)都具有相同的约束。
当涉及到不同的操作系统时,可以考虑使用抽象工厂模式。
4. Builder模式:将复杂对象的构造与其表示分离,以便相同的构造过程可以
创建不同的表示。
1 公共类构建{
2 静态类Student{
3 字符串名称 = null ;
4 整数 = ‐1 ;
5 字符串性别 = null ;
6名公共学生(Builder建造者)
7 {
8 this.name=builder.name;
9 this.number=builder.number;
10 this.sex=builder.sex;
11}
12 静态类生成器{
13 字符串名称 = null ;
14 整数 = ‐1 ;
15 字符串性别 = null ;
16 公共生成器 setName(字符串名称){
17 this.name=名称;
18 返回这个;
19}
20 公共生成器 setNumber(int number){
21 this.number=number;
22 返回这个;
23 }
24 public Builder setSex(String sex){25 this.sex=sex;
26 返回这个;
27}
28 公共学生构建(){
29 返回新的Student(this);
30}
31
32}
33}
34 public static void main(String[] args) {
35 Student A=new Student.Builder().setName("张
三").setNumber(1).build();
36 Student B=new Student.Builder().setSex("男").setName("李
4").build();
37 System.out.println(A.姓名+" "+A.号码+" "+A.性别);
38 System.out.println(B.姓名+" "+B.号码+" "+B.性别);
39}
40}
使用场景:
1、当同一个方法但不同的执行顺序产生不同的事件结果时,可以使用构建器模型
模式。
2. 多个组件或零件可以组装成一个对象,但运行结果不同。
,可以使用该模式。
3、产品类非常复杂,或者产品类中不同的调用序列产生不同的效率。在这种情况下
使用构建器模式是完美的。
5.原型模式:使用原型实例来指定要创建的对象类型,并通过复制这些原型来创建新的对象
目的。
1 公共类 Prototype 实现 Cloneable{
2 私有字符串名称;
3 公共字符串 getName() {
4 返回姓名;
5}
6 公共无效setName(字符串名称){
7 this.name = 名称;8 }
9 @覆盖
10 受保护的对象克隆() {
11 尝试{
12 返回 super.clone();
13 } catch (CloneNotSupportedException e) {
14 e.printStackTrace();
15}终于{
16 返回空值;
17}
18}
19 公共静态无效主(字符串[]参数){
20 原型 pro = new 原型();
21 原型 pro1 = (原型)pro.clone();
22 }
23 }
原型模式实际上实现了Cloneable接口并重写了clone()方法。
使用原型模式的优点:
1.性能优良
原型模式是在内存中复制一个二进制流,它比直接new一个对象有更好的性能,尤其是
当循环体内生成大量对象时,原型模式更能体现其优势。
2. 逃脱构造函数的约束
这既是它的优点,也是它的缺点。如果直接复制到内存中,构造函数将不会被执行(参见
第 13.4 节)。
使用场景:
1. 资源优化场景
类初始化需要消化很多资源,包括数据、硬件资源等。
2、有性能和安全要求的场景
通过new生成对象需要非常繁琐的数据准备或者访问权限,所以可以使用原型模型
模式。
3、一个对象多个修饰符的场景。当一个对象需要被其他对象访问,并且每个调用者都可能需要修改它的值时,可以
考虑使用原型模式复制多个对象以供调用者使用。
浅拷贝和深拷贝:
浅拷贝:Object类提供的clone方法只复制对象以及对象内部的数组和引用
该对象没有被复制,但它仍然指向原始对象的内部元素地址。这种类型的复制称为浅复制。
其他原始类型如 int、long、char、string(作为原始类型)等将被复制。
贝。
注意:使用原型模式时,引用的成员变量必须满足两个条件才不会被复制: 1.
它是类的成员变量,而不是方法内的变量;其次,它必须是一个可变引用对象,而不是
原始类型或不可变对象。
深拷贝:对私有类变量进行独立的拷贝
例如:this.arrayList = (ArrayList)this.arrayList.clone();
6、适配器模式:将一个类的接口转变成客户端期望的另一个接口,从而使原来的
由于接口不匹配而无法一起工作的两个类可以一起工作。
主要可分为3种:
1.类适配:创建一个新的类,继承源类,并实现新的接口,如
1 类适配器扩展 oldClass 实现 newFunc{}
2.对象适配:创建一个新的类来维护源类的实例并实现新的接口,如
1 类适配器实现 newFunc { private oldClass oldInstance
;}
3、接口适配:创建一个新的抽象类来实现旧的接口方法。例如
1 抽象类适配器实现oldClassFunc { void
newFunc();}
使用场景:
当您有动力修改已经投入生产的接口时,适配器模式可能是最适合您的模式。比较
如果系统进行扩展,需要使用现有的或新创建的类,但该类不满足系统的接口。
嘴,我该怎么办?使用适配器模式,我们的示例中也提到了这一点。
7、装饰者模式:动态地给一个对象添加一些额外的职责。在增加功能、装饰方面
容器模式比生成子类更加灵活 1 interface Source{ void method();}
2 公共类Decorator实现Source{
3.私人Source源码;
4 公共无效装饰1(){
5 System.out.println("装饰");
6}
7 @覆盖
8 公共无效方法(){
9 装饰1();
10 源.method();
11}
12}
使用场景:
1、需要扩展类的功能或者给类添加额外的功能。
2、需要动态地给一个对象添加功能,并且这些功能是可以动态撤销的。
3、如果需要对一批兄弟进行修改或添加功能,当然装饰模式是首选。
8、代理模式:为其他对象提供代理来控制对该对象的访问。
1个接口来源{
2 无效方法();
3}
4 类 OldClass 实现 Source{
5 @覆盖
6 公共无效方法(){
7}
8}
9类Proxy实现Source{
10 private Source source = new OldClass();
11 无效 doSomething(){}
12 @覆盖
13 公共无效方法(){
14 new Class1().Func1();
15. 源方法();
16 new Class2().Func2();
17 doSomething();18 }
19}
9. 中介者模式:使用中介者对象来封装一系列对象交互。中介者消除了每个对象的需要
显式交互,以便它们松散耦合,并且可以独立更改它们的交互。
1 公共抽象类Mediator {
2 //定义同事类
3 受保护的ConcreteColleague1 c1;
4 受保护的ConcreteColleague2 c2;
5 //通过getter/setter方法注入同事类
6 公共 ConcreteColleague1 getC1() {
7 返回c1;
8}
9 公共无效 setC1(ConcreteColleague1 c1) {
10 这个.c1 = c1;
11}
12 公共 ConcreteColleague2 getC2() {
13 返回c2;
14}
15 公共无效setC2(ConcreteColleague2 c2){
16 这个.c2 = c2;
17}
18 //中介模型的业务逻辑
19 公共抽象无效 doSomething1();
20 公共抽象无效 doSomething2();
21 }
使用场景:
中介者模式适用于多个对象紧密耦合的情况。紧耦合的标准是:在类图中
出现蜘蛛网状的结构,即每个类都与其他类直接相关。
10.命令模式:将请求封装成一个对象,允许使用不同的请求
参数化、对请求进行排队或记录请求日志,可以提供命令撤消和恢复功能。
接收者角色:该角色是执行工作的角色。这里传递的命令是应该执行的Command命令角色:所有需要执行的命令都在这里声明。
Invoker调用者角色:接收命令并执行命令
1 //通用接收器类
2 公共抽象类接收器{
3 公共抽象无效 doSomething();
4}
5 //具体接收者类
6 公共类ConcreteReciver1扩展接收器{
7 //每个接收者必须处理一定的业务逻辑
8 公共无效 doSomething(){ }
9}
10 公共类ConcreteReciver2扩展接收器{
11 //每个接收者必须处理一定的业务逻辑
12 公共无效 doSomething(){ }
13}
14 //抽象Command类
15 公共抽象类命令{
16 公共抽象无效执行();
17}
18 //具体命令类
19 公共类ConcreteCommand1扩展命令{
20 //在哪个Receiver类上执行命令处理?
21 私人接收器接收器;
22 //构造函数传递接收者
23 公共ConcreteCommand1(接收器_receiver){
24 this.receiver = _receiver;
25}
26 //必须执行一个命令
27 公共无效执行(){
28 //业务处理
29 this.receiver.doSomething();
30}
31}
32 公共类ConcreteCommand2扩展命令{
33 //哪个Receiver类执行命令处理 34 private Receiver receive;
35 //构造函数传递接收者
36 公共ConcreteCommand2(接收器_receiver){
37. 接收器 = _receiver;
38}
39 //必须执行一个命令
40 公共无效执行(){
41 //业务处理
42. 接收器.doSomething();
43}
44}
45 //调用者类
46 公共类调用者{
47 私人指挥指挥;
48
49 public void setCommand(命令_command){
50 this.command = _command;
51}
52
53 公共无效行动(){
54 this.command.execute();
55}
56}
57 //场景类
58 公共类客户端{
59 public static void main(字符串[] args){
60 Invoker调用者 = new Invoker();
61 接收器receiver = new ConcreteReceiver1();
62
63 Command command = new ConcreteCommand1(receiver);
64 调用者.setCommand(命令);
65 调用者.action();
66}
67}
使用场景:命令模式可以用在被认为是命令的地方。例如,在 GUI 开发中,单击按钮
是一个命令,可以处于命令模式;模拟DOS命令时,当然也必须使用命令模式。