1、什么是设计模式

2、常用几种设计模式的特点

3、设计模式要素

4、设计模式的七大原则

5、设计模式在Spring中的应用

6、JDK中使用到的23个经典设计模式

Spring中经典的9种设计模式

1.简单工厂

2.工厂方法

3.单例模式

4.适配器模式

5.装饰器模式

6.代理模式

7.观察者模式

8.策略模式

9.模版方法模式

1.什么是设计模式

设计模式(Design Pattern)是一套 多数人知晓、被反复使用、经过分类编目的、代码设计经验的总结。

使用设计模式是为了提高代码的可靠性、可重用性和可读性、让代码更容易被他人理解。

使用设计模式是为了提高代码的可重用性和可读性,增强系统的可靠性和可维护性,让代码更容易被他人理解,解决一系列的复杂问题,提高协作效率。

设计模式用于描述如何组织代码和使用最佳实践来解决常见的问题。

设计模式是高层次的设计方案,与具体实现细节无关(如算法,数据结构,网页等)。

设计模式一般有如下几个基本要素:

模式名称、问题、目的、解决方案、效果、实例代码和相关设计模式,

其中的关键元素包括以下四个方面:

模式名称 ,问题 ,解决方案 ,效果 。

1 、模式名称:一个助记名称,用来描述设计模式、解决方案和效果。

2、问题:主要描述在何时使用设计模式。

3 、解决方案:描述了设计的组成成分、它们之间的相互关系及各自的职责和协作方式

4、 效果:描述了模式应用的效果和使用模式权衡的问题。

设计模式使代码编制真正工程化;

设计模式是软件工程的基石脉络,如同大厦的结构一样。

2.设计模式的分类

根据目的(模式是用来做什么的)可分为 创建型,结构型和行为型 三类:

创建型模式:主要用于创建对象。

结构型模式:主要用于处理类和对象的组合。

行为型模式:主要用于描述类或对象如何交互和怎样分配职责。

根据范围,即模式主要是处理类之间的关系还是处理对象之间的关系,可分为类模式和对象模式两种。

类模式处理类和子类之间的关系,这些关系通过继承建立,在编译时刻就被确定下来,是一种静态关系;

对象模式处理对象间的关系,这些关系在运行时变化,更具动态性。

3.设计模式的优点

融合了众多专家的经验,并以一种标准的形式供广大开发人员所用提供了一套通用的设计词汇和一种通用的语言,以方便开发人员之间进行沟通和交流,使得设计方案更加通俗易懂让人们可以更加简单方便地复用成功的设计和体系结构,使得设计方案更加灵活,且易于修改,将提高软件系统的开发效率和软件质量,在一定程度上节约设计成本。

有助于初学者更深入地理解面向对象思想,方便阅读和学习现有类库与其他系统中的源代码,还可以提高软件的设计水平和代码质量。

4.常用几种设计模式的特点

在这里插入图片描述

5.设计模式的七大原则

设计模式(面向对象)有七大原则,分别是:

1.开放-封闭原则

2.单一职责原则

3.依赖倒转原则

4.迪米特法则(也称为最小知识原则)

5.接口隔离原则

6.合成/聚合复用原则

7.里氏代换原则

开放-封闭原则具有理想主义的色彩,他是面向对象设计的终极目标。

其他几条则可以看做是开放-封闭原则的实现方法。

设计模式就是实现了这些原则,从而达到了代码复用,增加可维护性的目的。

一.开放-封闭原则

概念:一个软件实体如类、模块和函数应该对扩展开放,对修改关闭。

模块应该尽量在不修改原代码的情况下进行扩展。

在软件周期内,因为变化、升级和维护等原因需要对软件原有代码进行修改时,可能会给代码引入错误,也可能会使我们不得不对整个功能进行重构,并且需要原有代码经过重新测试。当软件需求变化时,尽量通过扩展软件实体的行为来实现变化,而不是通过修改已有代码来实现变化。

开放封闭原则是面向对象设计的核心所在,遵循这个原则可以带来面向对象技术所声称的巨大好处,也就是可维护、可扩展、可复用、灵活性好。开发人员应该仅对程序中呈现的频繁变化的那些部分作出抽象,然而,对于应用程序中的每个部分都刻意的进行抽象同样不是一个好主意。拒绝不成熟的抽象和抽象本身一样重要。

注意事项:

1.通过接口或者抽象类约束扩展,对扩展进行边界限定,不允许出现在接口或抽象类中不存在的public方法。

2.参数类型、引用对象尽量使用接口或者抽象类,而不是实现类。

3.抽象层尽量保持稳定,一旦确定不允许修改。

二.单一职责原则

概念:就一个类而言,应该仅有一个引起它变化的原因。

### 当我们在做编程的时候,很自然的会一个类加上各种各样的功能。这样意味着,无论任何需求要来,你都需要更改这个类,这样其实是很糟糕的,维护麻烦,复用不可能,也缺乏灵活性。

如果一个类承担的职责过多,就等于把这些职责耦合起来,一个职责变化可能会削弱或者抑制这个类完成其他职责的能力。这种耦合会导致脆弱的设计,当变化发生时,设计会遭到很多意想不到的破坏。

三.依赖倒转原则

概念:依赖倒转原则是程序要依赖于抽象接口,不要依赖于具体实现。

简单的来说就是要求对抽象进行编程,不要对实现进行编程,这样就降低了客户与实现模块的耦合。

有时候为了代码复用,一般会把常用的代码写成函数或类库。这样开发新项目的时候直接用就行了。比如做项目的时候大多要访问数据库,所以我们把访问数据库的代码写成了函数。每次做项目去调用这些函数。那么问题来了,我们要做新项目的时候,发现业务逻辑高层模块都是一样的,但客户却希望使用不同的数据库或存储方式,这时就出现了麻烦。我们希望能再次利用这些高层模块,但是高层模块都是与低层的访问数据库绑定在一起,没办法复用这些高层的模块。所以不管是高层模块和底层模块都应该依赖于抽象,具体一点就是接口或者抽象类,只要接口是稳定的,那么任何一个更改都不用担心。

注意事项:

1.高层模块不应该依赖于低层模块。两个都应该依赖抽象。

2.抽象不应该依赖细节。细节应依赖于抽象。

四.迪米特法则(也称为最小知道原则)

概念:一个软件实体应当尽可能的少与其他实体发生相互作用。每一个软件单位对其他软件单位都只有最少的知识,而且局限于那些与本单位密切相关的软件单位。迪米特法则的初衷在于降低类之间的耦合。由于每个类尽量减少对其他类的依赖,因此,很容易使得系统的功能模块功能独立,相互之间不存在(或很少有)依赖关系。迪米特法则不希望类之间建立直接的联系。如果有真的需要建立联系的,也希望能通过他的友元类来转达。因此,应用迪米特法则有可能造成一个后果就是:系统中存在大量的中介类,这些类之所以存在完全是为了传递类之间的相互关系,这在一定程度上增加了系统的复杂度。

五.接口隔离原则

概念:客户端不应该依赖他不需要的接口,类间的依赖关系应建立在最小的接口上。

接口隔离原则的核心定义,不出现臃肿的接口,但是“小”是有限度的,首先就是不能违反单一职责原则。

六.合成/聚合复用原则

概念:合成/聚合复用原则经常又叫做合成复用原则,就是在一个新的对象里面使用一些已有的对象,使之成为新对象的一部分,新的对象通过这些对象的委派达到复用已有功能的目的。他的设计原则是:要尽量使用合成/聚合,尽量不要使用继承。

七.里氏代换原则

概念:里氏代换原则是面向对象设计的基本原则之一。即任何基类可以出现的地方,子类一定可以出现。里氏代换原则是继承复用的基石,只有当衍生类可以替换掉基类,软件单位的功能不受影响时,基类才能被真正复用,而衍生类也能够在积累的基础上增加新的行为,里氏代换原则是对“开-闭”原则的补充。实现“开-闭”原则的关键步骤就是抽象化。在基类与子类的继承关系就是抽象化的具体实现,所以里氏代换原则是对实现抽象化的具体步骤的规范。

当满足继承的时候,父类肯定存在非私有的成员,子类肯定是得到了父类的这些非私有成员(假设,父类的成员全部是私有的,那么子类没办法从父类继承任何成员,也就不存在继承的概念了)。既然子类继承了父类的这些非私有成员,那么父类对象也就可以在子类对象中调用这些非私有成员。所以,子类对象可以替换父类对象的位置。

在里氏替换原则下,当需求有变化时,只需继承,而别的东西不会改变。由于里氏代换原则才使得开放封闭称为可能。这样使得子类在父类无需修改就可以扩展。

JDK中使用到的23个经典设计模式

A. 创建模式

Singleton(单态):只允许一个实例

    Runtime.getRuntime();
    java.awt.Toolkit.getDefaultToolkit();
    java.awt.GraphicsEnvironment.getLocalGraphicsEnvironment();
    java.awt.Desktop.getDesktop();

Abstract factory(抽象工厂):创建一组有关联的对象实例

    java.util.Calendar.getInstance();
    java.util.Arrays.asList();
    java.util.ResourceBundle.getBundle(String baseName);
    java.sql.DriverManager.getConnection(String url);
    java.sql.Connection.createStatement();
    java.sql.Statement.executeQuery(String sql);
    java.text.NumberFormat.getInstance();
    javax.xml.transform.TransformerFactory.newInstance();

Factory(工厂方法):按照需求返回一个类型的实例

    java.lang.Proxy.newProxyInstance();
    java.lang.Object.toString();
    java.lang.Class.newInstance();
    java.lang.reflect.Array.newInstance();
    java.lang.reflect.Constructor.newInstance();
    java.lang.Boolean.valueOf(String s);
    java.lang.Class.forName(String className);

Builder(建造者):主要用来简化一个复杂的对象的创建

    java.lang.StringBuilder.append(Object obj);
    java.lang.StringBuffer.append(Object obj);  
    java.sql.PreparedStatement  
    javax.swing.GroupLayout.Group.addComponent();

Prototype(原型):使用自己的实例创建另一个实例

    java.lang.Object.clone();
    java.lang.Cloneable;

B.结构模式

Adapter(适配器):一个接口或类变成另外一种形式

    java.util.Arrays.asList();
    javax.swing.JTable(TableModel)  
    java.io.InputStreamReader(InputStream)  
    java.io.OutputStreamWriter(OutputStream)  
    javax.xml.bind.annotation.adapters.XmlAdapter.marshal();
    javax.xml.bind.annotation.adapters.XmlAdapter.unmarshal();

Composite(组合):让使用者把单独的对象和组合对象混用

    javax.swing.JComponent.add(Component comp);
    java.awt.Container.add(Component comp); 
    java.util.Map.putAll(Map m);
    java.util.List.addAll(Collection c);
    java.util.Set.addAll(Collection c);

Decorator(装饰器):为一个对象动态的加上一系列的动作,而不需要因为这些动作的不同而产生大量的继承类

    java.io.BufferedInputStream(InputStream);
    java.io.DataInputStream(InputStream);
    java.io.BufferedOutputStream(OutputStream);
    java.util.zip.ZipOutputStream(OutputStream);
    java.util.Collections.checkedList(List list, Class type) ; 

Facade(门面):一个简单的接口包装一组组件,接口,抽象或是子系统。

    java.lang.Class  
    javax.faces.webapp.FacesServlet

Flyweight(共享元):有效率地存储大量的小的对象

    java.lang.Integer.valueOf(int)  
    java.lang.Boolean.valueOf(boolean)  
    java.lang.Byte.valueOf(byte)
    java.lang.Character.valueOf(char)

Proxy(代理):用一个简单的对象来代替一个复杂的对象

     java.lang.reflect.Proxy

C.行为模式

Chain of Responsibility(责任链):一个对象在一个链接传递直到被处理

    java.util.logging.Logger.log() 
    javax.servlet.Filter.doFilter() 

Command(命令):将一系列的命令封装成在一个类中

    java.lang.Runnable
    javax.swing.Action

Interpreter(解释器):定义语言的文法 ,并且建立一个解释器来解释该语言中的句子

    java.util.Pattern
    java.text.Normalizer
    java.text.Format  

Iterator(迭代器):对象遍历

    java.util.Iterator
    java.util.Enumeration

Mediator(中介):用一个中介对象来封装一系列关于对象交互行为.

    java.util.Timer
    java.util.concurrent.Executor.execute()
    java.util.concurrent.ExecutorService.submit()
    java.lang.reflect.Method.invoke()

Memento(快照):保存另外一个对象内部状态拷贝的对象

    java.util.Date
    java.io.Serializable

Observer(观察者):事件监听器

    java.util.EventListener
    javax.servlet.http.HttpSessionBindingListener
    javax.servlet.http.HttpSessionAttributeListener
    javax.faces.event.PhaseListener 

State(状态):不同的状态,不同的行为;或者说,每个状态有着相应的行为.

    java.util.Iterator
    javax.faces.lifecycle.LifeCycle.execute()

Strategy(策略):定义一系列的算法,把这些算法一个个封装成单独的类

    java.util.Comparator.compare()
    javax.servlet.http.HttpServlet 
    javax.servlet.Filter.doFilter()

Template(模板):定义一个操作中算法的骨架,将一些步骤的执行延迟到其子类中

    java.util.Collections.sort()
    java.io.InputStream.skip() 
    java.io.InputStream.read()
    java.util.AbstractList.indexOf() 

Visitor(访问者):作用于某个对象群中各个对象的操作. 它可以使你在不改变这些对象本身的情况下,

定义作用于这些对象的新操作.

    javax.lang.model.element.Element 和javax.lang.model.element.ElementVisitor
    javax.lang.model.type.TypeMirror 和javax.lang.model.type.TypeVisitor  

设计模式在Spring中的应用

设计模式是软件开发中经过验证的解决方案,用于解决常见的设计问题。

Spring框架是一个功能强大的Java开发框架,它广泛应用了各种设计模式。

本文将介绍一些在Spring中常见的设计模式,并提供相应的Java示例。

1. 单例模式(Singleton Pattern)

单例模式确保一个类只有一个实例,并提供全局访问点。

在Spring中,Bean默认是单例的,即每个Bean定义只会创建一个实例。这种方式可以节省资源并提高性能。

示例代码:

@Service
public class MyService {
    // ...
}

2. 工厂模式(Factory Pattern)

工厂模式用于创建对象,而不需要直接调用构造函数。

在Spring中,BeanFactory是一个工厂模式的实现,它负责创建和管理Bean实例。

示例代码:

@Configuration
public class AppConfig {
    @Bean
    public MyService myService() {
        return new MyService();
    }
}

3. 代理模式(Proxy Pattern)

代理模式用于控制对其他对象的访问。

在Spring中,AOP(面向切面编程)使用代理模式来实现横切关注点的功能,例如日志记录、事务管理等。

示例代码:

@Aspect
@Component
public class LoggingAspect {
    @Before("execution(* com.example.MyService.*(..))")
    public void beforeMethod(JoinPoint joinPoint) {
        System.out.println("Before method: " + joinPoint.getSignature().getName());
    }
}

4. 观察者模式(Observer Pattern)

观察者模式定义了一种一对多的依赖关系,当一个对象的状态发生变化时,它的所有依赖对象都会收到通知并自动新。

在Spring中,事件机制使用观察者模式来实现。

示例代码:

@Component
public class MyEventPublisher {
    @Autowired
    private ApplicationEventPublisher eventPublisher;
    
    public void publishEvent() {
        MyEvent event = new MyEvent(this);
        eventPublisher.publishEvent(event);
    }
}

@Component
public class MyEventListener implements ApplicationListener<MyEvent> {
    @Override
    public void onApplicationEvent(MyEvent event) {
        System.out.println("Received event: " + event);
    }
}

5. 模板方法模式(Template Method Pattern)

模板方法模式定义了一个算法的骨架,将一些步骤的实现延迟到子类中。

在Spring中,JdbcTemplate是一个模板方法模式的应用,它封装了JDBC操作的通用逻辑,而具体的SQL语句和参数由子类实现。

示例代码:

@Repository
public class MyRepository {
    @Autowired
    private JdbcTemplate jdbcTemplate;
    
    public void saveData(String data) {
        jdbcTemplate.update("INSERT INTO my_table (data) VALUES (?)", data);
    }
}

6. 适配器模式(Adapter Pattern)

适配器模式用于将一个类的接口转换为客户端所期望的另一个接口。

在Spring中,适配器模式经常用于将现有的类集成到Spring框架中。

示例代码:

public interface AService {
    void doSomething();
}

public class AServiceImpl implements AService {
    @Override
    public void doSomething() {
        // 实现具体的业务逻辑
    }
}

public class BServiceAdapter implements InitializingBean, AService {
    private AServiceImpl aServiceImpl;
    
    @Override
    public void afterPropertiesSet() {
        aServiceImpl= new  AServiceImpl ();
    }
    
    @Override
    public void doSomething() {
        aServiceImpl.doSomething();
    }
}

这只是Spring中一些常见的设计模式示例,还有其他设计模式如策略模式、装饰器模式等也在Spring中得到应用。
通过使用这些设计模式,Spring提供了强大的功能和灵活性,使得开发人员能够更好地构建可扩展和可维护的应用程序。

Spring 中经典的 9 种设计模式

简单工厂模式

首先,我们定义一个抽象产品类 Product,它包含一个抽象方法 use(),表示产品的使用方法。

/**
 * 建档工厂模式
 * 首先,我们定义一个抽象产品类 Product,它包含一个抽象方法 use(),表示产品的使用方法。
 */
public abstract class Product {
    public abstract void use();
}

然后,我们定义三个具体产品类 ProductA 、ProductB和ProductC,它们都继承自抽象产品类 Product,并实现了 use() 方法。

/**
 * 产品A
 * 然后,我们定义两个具体产品类 ProductA,它们都继承自抽象产品类 Product,并实现了 use() 方法
 */
public class ProductA extends Product{
    @Override
    public void use() {
      System.out.println("正在使用A产品~");
    }
}

/**
 * 产品B
 * 然后,我们定义两个具体产品类 ProductB,它们都继承自抽象产品类 Product,并实现了 use() 方法
 */
public class ProductB extends Product{
    @Override
    public void use() {
        System.out.println("正在使用B产品~");
    }
}

/**
 * 产品c
 * 然后,我们定义两个具体产品类 ProductC,它们都继承自抽象产品类 Product,并实现了 use() 方法
 */
public class ProductC extends Product{
    @Override
    public void use() {
        System.out.println("正在使用c产品~");
    }
}

接下来,我们定义一个简单工厂类 ProductFactory,它包含一个静态方法 createProduct(String type),根据传入的参数 type 来创建对应的产品实例。如果传入的参数不是 “A” 、 "B"或 “C”,则返回 null。

/**
 * 产品工厂
 * 接下来,我们定义一个简单工厂类 ProductFactory,它包含一个静态方法 createProduct(String type),
 * 根据传入的参数 type 来创建对应的产品实例。如果传入的参数不是 "A" 或 "B"或 "C"
 */
public class ProductFactory {
   public static Product createProduct(String type){
       if("A".equals(type)){
           return new ProductA();
       }else if("B".equals(type)){
           return new ProductB();
       }else if("C".equals(type)){
           return new ProductC();
       }else{
           return null;
       }
   }
}

最后,我们可以在客户端代码中使用简单工厂来创建产品实例。

/**
 * 最后,我们可以在客户端代码中使用简单工厂来创建产品实例
 */
public class Client {
    public static void main(String[] args) {
        Product productA =ProductFactory.createProduct("A");
        productA.use();
        Product productB =ProductFactory.createProduct("B");
        productB.use();
        Product productC =ProductFactory.createProduct("C");
        productC.use();
    }
}

运行结果:

正在使用A产品~

正在使用B产品~

正在使用c产品~

简单工厂模式实际应用示例:

public abstract  class AutoDoor {
    /*闸机开门*/
    public abstract void openDoor();
    /*闸机关门*/
    public abstract void closeDoor();
    /*闸机锁门*/
    public abstract void lockDoor();
}
/**
 * DH闸机
 */
public class Dahua extends AutoDoor{
    @Override
    public void openDoor() {
        System.out.println("DH闸机开门");
    }
    @Override
    public void closeDoor() {
        System.out.println("DH闸机关门");
    }
    @Override
    public void lockDoor() {
        System.out.println("DH闸机锁门");
    }
}
/**
 * HK闸机
 */
public class Hikvision extends AutoDoor{
    @Override
    public void openDoor() {
        System.out.println("HK闸机开门");
    }
    @Override
    public void closeDoor() {
        System.out.println("HK闸机关门");
    }
    @Override
    public void lockDoor() {
        System.out.println("HK闸机锁门");
    }
}
/*YT*/
public class intellif extends AutoDoor{
    @Override
    public void openDoor() {
        System.out.println("YT闸机开门");
    }
    @Override
    public void closeDoor() {
        System.out.println("YT闸机关门");
    }
    @Override
    public void lockDoor() {
        System.out.println("YT闸机锁门");
    }
}
public class AutoDoorFactory {
    public static AutoDoor operateDoor(String type){
        if("Dahua".equals(type)){
            return new Dahua();
        }else if("Hikvision".equals(type)){
            return new Hikvision();
        }else if("intellif".equals(type)){
            return new intellif();
        }else{
            return null;
        }
    }
}
public class Company {
    public static void main(String[] args) {
      AutoDoor dahua = AutoDoorFactory.operateDoor("Dahua");
      dahua.openDoor();
      dahua.closeDoor();
      dahua.lockDoor();

      AutoDoor hikvision = AutoDoorFactory.operateDoor("Hikvision");
      hikvision.openDoor();
      hikvision.closeDoor();
      hikvision.lockDoor();

      AutoDoor intellif = AutoDoorFactory.operateDoor("intellif");
      intellif.openDoor();
      intellif.closeDoor();
      intellif.lockDoor();
    }
}

运行结果:

DH闸机开门

DH闸机关门

DH闸机锁门

HK闸机开门

HK闸机关门

HK闸机锁门

YT闸机开门

YT闸机关门

YT闸机锁门

总结:此时当我们想要添加其他厂商的时候,我们也需要创建一个类,去继承接口,此时我们的工厂类需要修改匹配厂商类型方法代码了,违反了开闭原则(扩展开放、修改关闭),我们应该扩展,不应该修改。

工厂方法模式

工厂方法模式,每种类型的汽车都有自己的工厂,可以横向扩展,实现了开闭原则。

1、创建生产汽车的工厂接口

public interface CarFactory {
  public  Car  makeCar();
}

2、宝马汽车工厂实现了生产汽车接口

public class BaoMaFactory implements CarFactory{
     @Override
    public Car makeCar() {
        return new BaoMaCar();
    }
}

3、奔驰汽车工厂实现了生产汽车接口

public class BenChiFactory implements CarFactory{
    @Override
    public Car makeCar() {
        return new BenChiCar();
    }
}

4、宝马汽车工厂在造车

public class BaoMaCar{
    public void  make() {
        System.out.println("宝马在造车......");
    }
}

5、奔驰汽车工厂在造车

public class BenChiCar{
    public void  make() {
        System.out.println("奔驰在造车......");
    }
}

运行结果:

public class Demo {
    public static void main(String[] args) {
        CarFactory benChiFactory = new BenChiFactory();
        benChiFactory.makeCar().make();

        CarFactory baoMaFactory = new BaoMaFactory();
        baoMaFactory.makeCar().make();
    }
}

宝马在造车…
奔驰在造车…

工厂方法模式-优点:

1、下次加入大众车,只需要新建一个 DazhongFactory(大众的工厂)就行,不会影响到别的工厂。

2、可以避免创建者和具体产品之间的紧密耦合。

3、单一职责原则。 你可以将产品创建代码放在程序的单一位置,从而使得代码更容易维护。

4、开闭原则。 无需更改现有客户端代码,就可以在程序中引入新的产品类型。

工厂方法模式-缺点:

代码量加大,管理复杂,结构复杂,实际业务一般使用简单工厂模式

工厂方法模式适合应用场景

核心 Java 程序库中有该模式的应用:

java.util.Calendar#getInstance()
java.util.ResourceBundle#getBundle()
java.text.NumberFormat#getInstance()
java.nio.charset.Charset#forName()
java.net.URLStreamHandlerFactory#createURLStreamHandler(String) (根据协议返回不同的单例对象)
java.util.EnumSet#of()
javax.xml.bind.JAXBContext#createMarshaller() 及其他类似的方法。

识别方法: 工厂方法可通过构建方法来识别,它会创建具体类的对象,但以抽象类型或接口的形式返回这些对象。

总结:不一定要符合设计原则(源码与设计模式有差别),要根据实际情况加以分析取舍

适配器模式

什么是适配器模式?

适配器模式是一种结构型设计模式。

适配器模式:当接口无法和类匹配到一起工作时,通过适配器将接口变换成可以和类匹配到一起工作的接口。

打个比喻:有一个电器的插头是三脚的,而现有的插座是两孔的,要使插头插上插座,我们需要一个插头转换器,这个转换器即是适配器。

源(Adaptee):需要被适配的对象或类型,相当于插头。

适配器(Adapter):连接目标和源的中间对象,相当于插头转换器。

目标(Target):期待得到的目标,相当于插座。

这是我们原来存在的代码

class Adaptee{
    public void method1(){
        System.out.println("这是原来的类,里面存在method1");
    }
}

这是客户需要的目标接口

interface Target{
    public void method1();
    public void method2();
}

原来的类无法满足客户的需求,这时候就需要一个适配器来让两者相匹配。

(之前我会不懂,为什么不在原来的类上去修改?因为原来的类被用了这么久,肯定是正确的,你在原来的类上修改,当类里面的代码很多,不容易维护。建一个适配器就很容易定位到错误在哪里)

适配器模式分类:

类适配器模式:类适配器使用的是继承的方式,一般来说无法对其子类进行适配

对象适配器模式:对象适配器使用的是组合的方式,子孙类都可以被适配。

另外,对象适配器对于增加一些新行为非常方便,而且新增加的行为同时适用于所有的源。

接口适配器模式(又称缺省适配器模式):接口适配器模式(缺省适配模式)基本思想是,为一个接口提供缺省实现,这样子类可以从这个缺省实现进行扩展,而不必从原有接口进行扩展。

举例:

类适配器模式,从名字可以看出来直接和类相关,通过继承类,实现接口来达到需求。

由于是继承的方式来得到适配器类,所以无法再对其子类进行适配。

————————————————

class Adaptee{//源码
    public void method1(){
        System.out.println("这是原来的类,里面存在method1方法");
    }
}

interface Target{//目标接口
    public void method1();
    public void method2();
}

class Adapter extends Adaptee implements Target{//适配器类
    @Override
    public void method2() {
        System.out.println("这是接口中的方法。");
    }
}

现在我们来测试一下适配器
public class Test {
    public static void main(String[] args) {
        Target adapter = new Adapter();
        adapter.method1();
        adapter.method2();
    }
}

输出:

这是原来的类,里面存在method1方法。

对象适配器模式:根据文字来看,肯定是与对象有关。

对象适配器用的是组合的方式,将原有的类作为适配器的属性。

组合方式来得到的适配器,可以对原有类的子类进行适配,将它作为属性加入就可以了。

class Adaptee{//源码
    public void method1(){
        System.out.println("这是原来的类里面存在method1方法");
    }
}

interface Target{//目标接口
    public void method1();
    public void method2();
}

class Adapter implements Target{

Adaptee adaptee = new Adaptee();//组合方式

    @Override
    public void method1() {
        adaptee.method1();
    }

    @Override
    public void method2() {
        System.out.println("这是接口中的方法。");
    }
}

测试
public class Test {
    public static void main(String[] args) {
        Target adapter = new Adapter();
        adapter.method1();
        adapter.method2();
    }
}

输出

这是原来的类里面存在method1方法

这是接口中的方法。

接口适配器模式(缺省适配模式)

接口适配器模式与上面的有些区别,接口适配器注重于接口中用不到的抽象太多,全部多实现的话没有意义。

interface Target{//目标接口
    public void method1();
    public void method2();
    public void method3();
}

//定义一个抽象类,来选择需要重写的类
abstract class abstractAdaptee implements Target{
    public void method1(){
        System.out.println("默认实现");
    }
//这个方法必须由子类来重写
public abstract void method2();
    public void method3(){
        System.out.println("默认实现2");
    }
}

//定义一个类来继承abstractAdaptee类
//通过接口和抽象类的结合,我们在子类中可以选择性的去实现那些抽象方法
class Adapter extends abstractAdaptee{//
    @Override
    public void method2() {
        System.out.println("子类必须重写这个方法");
    }
}

适配器的优点与缺点:

优点:

复用性更强,对于那些和目标接口差别不是很大的类,通过适配器可以让这些类达到很好的复用,减少代码的书写。

并且在适配中可以添加一些方法来扩展系统的功能,扩展性更好.

缺点:

用多了会让系统看起来杂乱无章。比如:明明调用的是接口A,结构里面的内容以及被修改成了接口B。

一般建议直接对系统进行重构。

单例模式

采取一定的办法保证在整个软件系统中,确保对于某个类只能存在一个实例。

单例模式有如下三个特点:

①、单例类只能有一个实例

②、单例类必须自己创建自己的实例

③、单例类必须提供外界获取这个实例的方法

2、单例类的设计思想(Singleton)

①、必须将构造器私有化,外界不能创建这个类的实例。

public  class  Singleton {
 //构造器私有化
       private Singleton(){}
}

②、单例类必须自己创建自己的实例,不能允许在类的外部修改内部创建的实例。

比如将这个实例用 private 声明。为了外界能访问到这个实例,我们还必须提供 getInstance 方法得到这个实例。因为外界不能new这个类,所以我们必须用static来修饰字段和方法。

//在类的内部自己创建实例
private static Singleton singleton = new Singleton();
//提供get 方法以供外界获取单例
public  static  Singleton getInstance(){
  return singleton;
}

③、是否支持延迟加载?

有些情况下,创建某个实例耗时长,占用资源多,用的时候也少,我们会考虑在用到的时候才会去创建,这就是延迟加载(懒加载)。

但有些情况,按照 fail-fast 的设计原则(有问题及早暴露),比如某个实例占用资源很多,如果延迟加载,会在程序运行一段时间后OOM,如果在程序启动的时候就创建这个实例(饿汉式),我们就可以立即去修复,不会导致程序运行之后的系统奔溃。

所以,是否支持延迟加载需要结合实际情况考虑。

④、保证线程安全

这个是一定要考虑的,如果你写的单例类存在线程安全问题,那就是伪单例了。

3、单例类的几种实现方式

3.1 单例模式之饿汉模式

public class Singleton {
 //构造器私有化
 private Singleton(){}
 //在类的内部自己创建实例
 private static Singleton singleton = new Singleton();
 //提供get 方法以供外界获取单例
 public static Singleton getInstance(){
  return singleton;
 }
}

测试:

public static void main(String[] args) {
 Singleton s1 = Singleton.getInstance();
 Singleton s2 = Singleton.getInstance();
 System.out.println(s1.equals(s2)); //true
}

这种模式在类加载的时候实例singleton就已经创建并初始化好了,所以是线程安全的。

缺点:

1、不过这种模式不支持延迟加载;

2、有可能这个实例化过程很长,那么就会加大类装载的时间;

3、有可能这个实例现阶段根本用不到,那么创建了这个实例,也会浪费内存。

但是还是我们前面说的,是否支持延迟加载,需要结合实际情况考虑。

3.2 单例模式之懒汉模式(线程不安全)

//懒汉模式
public class Singleton {
 //构造器私有化
 private Singleton(){}
 //在类的内部自己创建实例的引用
 private static Singleton singleton = null;
 //提供get 方法以供外界获取单例
 public static Singleton getInstance(){
  if(singleton == null){
   singleton = new Singleton();
  }
  return singleton;
 } 
}

这种方法达到了 lazy-loading 的效果,即我们在第一次需要得到这个单例的时候,才会去创建它的实例,以后再需要就可以不用创建,直接获取了。但是这种设计在多线程的情况下是不安全的。

我们可以创建两个线程来看看这种情况:

public class ThreadSingleton extends Thread{
 @Override
 public void run() {
  try {
   System.out.println(Singleton.getInstance());
  } catch (Exception e) {
   e.printStackTrace();
  }
 }
 public static void main(String[] args) {
  ThreadSingleton s1 = new ThreadSingleton();
  s1.start(); //com.ys.pattern.Singleton@5994a1e9
  
  ThreadSingleton s2 = new ThreadSingleton();
  s2.start(); //com.ys.pattern.Singleton@40dea6bc
 }
}

很明显:最后输出结果的两个实例是不同的。

这便是线程安全问题。

那么怎么解决这个问题呢?

参考这篇博客:Java多线程同步:http://www.cnblogs.com/ysocean/p/6883729.html

3.3 单例模式之懒汉模式(线程安全)

这里我们采用同步代码块来达到线程安全

//懒汉模式线程安全
public class Singleton {
 //构造器私有化
 private Singleton(){}
 //在类的内部自己创建实例的引用
 private static Singleton singleton = null;
 //提供get 方法以供外界获取单例
 public static Singleton getInstance() throws Exception{
  synchronized (Singleton.class) {
   if(singleton == null){
    singleton = new Singleton();
   }
  }
  return singleton;
 }
}

我们给 getInstance() 方法创建实例时加了一把锁 synchronzed,这样会导致这个方法的并发为1,相当于串行操作,如果这个单例在实际项目中会频繁被调用,那就会频繁加锁,释放锁,会有性能瓶颈,不推荐此种方式。

3.4 单例模式之懒汉模式(线程安全)–双重校验锁

分析:上面的例子我们可以看到,synchronized 其实将方法内部的所有语句都已经包括了,每一个进来的线程都要单独进入同步代码块,判断实例是否存在,这就造成了性能的浪费。那么我们可以想到,其实在第一次已经创建了实例的情况下,后面再获取实例的时候,可不可以不进入这个同步代码块?

//懒汉模式线程安全--双重锁校验
public class Singleton {
 //构造器私有化
 private Singleton(){}
 //在类的内部自己创建实例的引用
 private static Singleton singleton = null;
 //提供get 方法以供外界获取单例
 public static Singleton getInstance() throws Exception{
  if(singleton == null){
   synchronized (Singleton.class) {
    if(singleton == null){
     singleton = new Singleton();
    }
   }
  }
  return singleton;
 }
}

以上的真的完美解决了单例模式吗?其实并没有,请看下面:

3.5 单例模式之最终版

我们知道编译就是将源代码翻译成机械码的过程,而Java虚拟机的目标代码不是本地机器码,而是虚拟机代码。编译原理里面有个过程是编译优化,就是指在不改变原来语义的情况下,通过调整语句的顺序,来让程序运行的更快,这个过程称为 reorder。

JVM 只是一个标准,它并没有规定有关编译器优化的内容,也就是说,JVM可以自由的实现编译器优化。

那么我们来再来考虑一下,创建一个变量需要哪些步骤?

①、申请一块内存,调用构造方法进行初始化

②、分配一个指针指向该内存

而这两步谁先谁后呢?

也就是存在这样一种情况:

1.先开辟一块内存,

2.然后分配一个指针指向该内存,

3.最后调用构造方法进行初始化。

那么针对单例模式的设计,就会存在这样一个问题:线程A开始创建Singleton的实例,此时线程B已经调用了 getInstance的()方法,首先判断 instance 是否为 null。而我们上面说的那种模型, A 已经把 instance 指向了那块内存,只是还没来得及调用构造方法进行初始化,因此 B 检测到 instance 不为 null,于是直接把 instance 返回了。那么问题出现了:尽管 instance 不为 null,但是 A 并没有构造完成,就像一套房子已经给了你钥匙,但是里面还没有装修,你并不能住进去。

解决方案:使用 volatile 关键字修饰 instance

我们知道在当前的Java内存模型下,线程可以把变量保存在本地内存(比如机器的寄存器)中,而不是直接在主存中进行读写。这就可能造成一个线程在主存中修改了一个变量的值,而另外一个线程还继续使用它在寄存器中的变量值的拷贝,造成数据的不一致。

volatile修饰的成员变量在每次被线程访问时,都强迫从共享内存中重读该成员变量的值。

而且,当成员变量发生变化时,强迫线程将变化值回写到共享内存。

这样在任何时刻,两个不同的线程总是看到某个成员变量的同一个值。

//懒汉模式线程安全--volatile
public class Singleton {
    //在类的内部自己创建实例的引用
    private static volatile Singleton singleton = null;
    //构造器私有化
    private Singleton(){}
    //提供get 方法以供外界获取单例
    public static Singleton getInstance() throws Exception{
        if(singleton == null){
            synchronized (Singleton.class) {
                if(singleton == null){
                    singleton = new Singleton();
                }
            }
        }
        return singleton;
    }

}

到此我们完美的解决了单例模式的问题。但是 volatile 关键字是 JDK1.5 才有的,也就是 JDK1.5 之前是不能这样用的

上面我们说要加关键字 volatile ,禁止指令重排,防止单例对象new 出来后,并且赋值给 singleton,但是还没来得及初始化这个问题。

现在高版本的 Java(JDK9) 已经在 JDK 内部实现中解决了这个问题,把对象的 new 操作和初始化操作设计为 原子操作。

相关参考链接:

https://shipilev.net/blog/2014/safe-public-construction

https://chriswhocodes.com/vm-options-explorer.html

3.7 单例模式之枚举类

public enum Singleton{
    INSTANCE;
    private Singleton(){}
}

通过Java枚举类的自身特性,保证实例创建的线程安全和唯一性。

3.8 单例模式之静态内部类

public class InnerSingleton {

    private InnerSingleton() {}

    static class Inner {
        static InnerSingleton instance = new InnerSingleton();
}

    public static InnerSingleton getInstance() {
        return Inner.instance;
    }

    public static void main(String[] args) {
        System.out.println(InnerSingleton.getInstance() == InnerSingleton.getInstance());//true
        System.out.println(InnerSingleton.getInstance().equals(InnerSingleton.getInstance()));//true

    }
}

4、单例模式的应用

单例模式在实际项目中有啥用呢?

还是根据其核心概念,某个数据在系统中只能存在一份,就可以设计为单例。

1、windows系统的回收站,我们能在任何盘符删除数据,但是最后都是到了回收站中

2、网站的计数器,不采用单例模式,很难实现同步;

3、数据库连接池,可以节省打开或关闭数据库连接所引起的效率损耗,用单例模式来维护,可以大大降低这种损耗。当然对于海量数据系统,会存在多个数据库连接池,比如一个能够快速执行SQL的连接池,还有一个是慢SQL,如果都放在一个池里面,会导致慢SQL执行的时候,长时间占用数据库连接资源,导致其他SQL请求无法响应。

4、系统的配置信息类,通常只存在一个。

模板方法模式

一:模板方法模式

1.什么是模板方法模式?

模板方法模式是一种行为设计模式, 它在超类中定义了一个算法的框架, 允许子类在不修改结构的情况下重写算法的特定步骤。

模板方法的基本介绍

1.模板方法模式(Template Method Pattern),又叫模板模式(Template Pattern),在一个抽象类公开定义了执行它的方法的模板。它的子类可以按需要重写方法实现,但调用将以抽象类中定义的方式进行。

2.简单说,模板方法模式定义了一个操作中的算法骨架,而将一些步骤延迟到子类中,使得子类可以不改变一个算法的结构,就可以重定义该算法的某些特定步骤。

3.模板方法模式适合应用场景

1.当你只希望客户端扩展某个特定算法步骤, 而不是整个算法或其结构时,可使用模板方法模式。

2.当多个类的算法除一些细微不同之外,几乎完全一样时,你可使用该模式。

3.但其后果就是,只要算法发生变化,你就可能需要修改所有的类。

4.模板方法模式优缺点

优点:

1.你可仅允许客户端重写一个大型算法中的特定部分,使得算法其他部分修改对其所造成的影响减小。

2.你可将重复代码提取到一个超类中。

缺点:

1.部分客户端可能会受到算法框架的限制。

2.通过子类抑制默认步骤实现可能会导致违反里氏替换原则。

3.模板方法中的步骤越多,其维护工作就可能会越困难。

4.模板方法模式角色:

抽象类(Abstract Class):负责给出一个算法的轮廓和骨架。它由一个模板方法和若干个基本方法构成。

模板方法:定义了算法的骨架,按某种顺序调用其包含的基本方法。(一般是按照某种顺序执行)

基本方法:实现算法各个步骤的方法,是模板方法的组成部分。

基本方法又可以分为三种:

1.抽象方法(Abstract Method) :一个抽象方法由抽象类声明、由其具体子类实现。

2.具体方法(Concrete Method) :一个具体方法由一个抽象类或具体类声明并实现,其子类可以进行覆盖也可以直接继承。

3.钩子方法(Hook Method) :在抽象类中已经实现,包括用于判断的逻辑方法和需要子类重写的空方法两种。

一般钩子方法是用于判断的逻辑方法,这类方法名一般为isXxx,返回值类型为boolean类型。

具体子类(Concrete Class):实现抽象类中所定义的抽象方法和钩子方法,它们是一个顶级

逻辑的组成步骤。

二.模板方法代码实现

//抽象类(包含模板方法和若干个基本方法)
public  abstract  class  AbstractClass {

//倒油步骤都一样,所以具体实现(具体方法)
	public void pourOil() {
		System.out.println("倒油");
	}
	//第二步:热油是一样的,所以直接实现(具体方法)
	public void heatOil() {
		System.out.println("热油");
	}
	//第三步:倒蔬菜是不一样的(一个下包菜,一个是下菜心)钩子方法
	public abstract void pourVegetable();
	//第四步:倒调味料是不一样
	public abstract void pourSauce();(钩子方法)
	//第五步:翻炒是一样的,所以直接实现(具体方法)
	public void fry(){
		System.out.println("炒啊炒啊炒到熟啊");
	}

	//模板方法按照一定顺序执行
	public final void cookProcess() {
		//第一步:倒油
		this.pourOil();
		//第二步:热油
		this.heatOil();
		//第三步:倒蔬菜
		this.pourVegetable();
		//第四步:倒调味料
		this.pourSauce();
		//第五步:翻炒
		this.fry();
	}
}

//具体子类
public   class   ConcreteClass_ BaoCai  extends  AbstractClass {

	@Override
	public void pourVegetable() {
		System.out.println("下锅的蔬菜是包菜");
	}
	@Override
	public void pourSauce() {
		System.out.println("下锅的酱料是辣椒");
	}
}

//具体子类
Public  class  ConcreteClass_ CaiXin  extends  AbstractClass {

	@Override
	public void pourVegetable() {
		System.out.println("下锅的蔬菜是菜心");
	}
	@Override
	public void pourSauce() {
		System.out.println("下锅的酱料是蒜蓉");
	}
}

//测试类
public class Client {
	public static void main(String[] args) {
		//炒手撕包菜
		ConcreteClass_BaoCai baoCai = new ConcreteClass_BaoCai();
		baoCai.cookProcess();//调用抽象类的模板方法
		//炒蒜蓉菜心
		ConcreteClass_CaiXin caiXin = new ConcreteClass_CaiXin();
		caiXin.cookProcess();//调用抽象类的模板方法
	}
}

运行结果:

倒油

热油

下锅的蔬菜是包菜

下锅的酱料是辣椒

炒啊炒啊炒到熟啊

倒油

热油

下锅的蔬菜是菜心

下锅的酱料是蒜蓉

炒啊炒啊炒到熟啊

装饰器模式(Decorator)

Decorator也称装饰者模式、装饰器模式、Wrapper、Decorator。

装饰模式是一种结构型设计模式, 允许通过将对象放入包含行为的特殊封装对象中来为原对象绑定新的行为。

解决方案

当你需要更改一个对象的行为时, 第一个跳入脑海的想法就是扩展它所属的类。 但是, 你不能忽视继承可能引发的几个严重问题。

继承是静态的。 你无法在运行时更改已有对象的行为, 只能使用由不同子类创建的对象来替代当前的整个对象。

子类只能有一个父类。 大部分编程语言不允许一个类同时继承多个类的行为。

其中一种方法是用聚合或组合,而不是继承。 两者的工作方式几乎一模一样: 一个对象包含指向另一个对象的引用,并将部分工作委派给引用对象; 继承中的对象则继承了父类的行为, 它们自己能够完成这些工作。

你可以使用这个新方法来轻松替换各种连接的 “小帮手” 对象, 从而能在运行时改变容器的行为。 一个对象可以使用多个类的行为, 包含多个指向其他对象的引用, 并将各种工作委派给引用对象。 聚合 (或组合) 组合是许多设计模式背后的关键原则 (包括装饰在内)。

封装器是装饰模式的别称, 这个称谓明确地表达了该模式的主要思想。 “封装器” 是一个能与其他 “目标” 对象连接的对象。 封装器包含与目标对象相同的一系列方法, 它会将所有接收到的请求委派给目标对象。但是,封装器可以在将请求委派给目标前后对其进行处理,所以可能会改变最终结果。

装饰模式适合应用场景

如果你希望在无需修改代码的情况下即可使用对象, 且希望在运行时为对象新增额外的行为, 可以使用装饰模式。

装饰能将业务逻辑组织为层次结构, 你可为各层创建一个装饰, 在运行时将各种不同逻辑组合成对象。 由于这些对象都遵循通用接口,客户端代码能以相同的方式使用这些对象。

如果用继承来扩展对象行为的方案难以实现或者根本不可行, 你可以使用该模式。

许多编程语言使用 final最终关键字来限制对某个类的进一步扩展。 复用最终类已有行为的唯一方法是使用装饰模式:用封装器对其进行封装。

装饰模式优缺点

优点:

你无需创建新子类即可扩展对象的行为。

你可以在运行时添加或删除对象的功能。

你可以用多个装饰封装对象来组合几种行为。

单一职责原则。 你可以将实现了许多不同行为的一个大类拆分为多个较小的类。

缺点:

在封装器栈中删除特定封装器比较困难。

实现行为不受装饰栈顺序影响的装饰比较困难。

各层的初始化配置代码看上去可能会很糟糕。

Java 示例代码:

abstract class Person {
    protected String name;
    public abstract void Operation(); // 职责
}

abstract class Decorator extends Person {
    protected Person person;
}

class DecoratorA extends Decorator {
    public DecoratorA(Person person) {
        this.person = person;
    }
    @Override
    public void Operation() { // 职责
        person.Operation(); // 原本的职责
        System.out.print("写作业 ");
    }
}

class DecoratorB extends Decorator {

    public DecoratorB(Person person) {
        this.person = person;
    }

    @Override
    public void Operation() { // 职责
        person.Operation(); // 原本的职责
        System.out.print("考试 ");
    }
}

class Student extends Person {
    public Student(String name) {
        this.name = name;
    }
    @Override
    public void Operation() {
        System.out.print(name + "的职责:学习 ");
    }
}

public class DecoratorPattern {

public static void main(String[] args) {

        Person zhangsan = new Student("张三");
        zhangsan = new DecoratorA(zhangsan);
        zhangsan = new DecoratorB(zhangsan);
        zhangsan.Operation();
        System.out.println("\n=====我是分割线=====");
        //对象链
        Person lisi = new DecoratorB(new DecoratorA(new Student("李四")));
}

}

创建一个接口:

步骤 1

Shape.java
public interface Shape { 
void draw(); 
}

步骤 2
创建实现接口的实体类。
Rectangle.java

public class Rectangle implements Shape { 
@Override 
public void draw() { 
System.out.println("Shape: Rectangle"); 
}  
} 

Circle.java

public class Circle implements Shape { 
@Override 
public void draw() { 
System.out.println("Shape: Circle"); 
} 
}

步骤 3
创建实现了 Shape 接口的抽象装饰类。
ShapeDecorator.java

public abstract class ShapeDecorator implements Shape { 
protected Shape dShape; 
public ShapeDecorator(Shape shape){ 
this.dShape= shape; 
} 
public void draw(){ 
dShape.draw(); 
} 
}

步骤 4
创建扩展了 ShapeDecorator 类的实体装饰类。
RedShapeDecorator.java

public class RedShapeDecorator extends ShapeDecorator { 
public RedShapeDecorator(Shape decoratedShape) { 
super(decoratedShape); 
} 

@Override 
public void draw() { 
decoratedShape.draw(); 
setRedBorder(decoratedShape); 
} 
private void setRedBorder(Shape decoratedShape){ 
System.out.println("Border Color: Red"); 
} 
}

步骤 5
使用 RedShapeDecorator 来装饰 Shape 对象。
DecoratorPatternDemo.java

public class DecoratorPatternDemo { 
public static void main(String[] args) { 
    Shape circle = new Circle(); 
    ShapeDecorator redCircle = new RedShapeDecorator(new Circle()); 
    ShapeDecorator redRectangle = new RedShapeDecorator(new Rectangle()); 
    //Shape redCircle = new RedShapeDecorator(new Circle()); 
    //Shape redRectangle = new RedShapeDecorator(new Rectangle()); 
    System.out.println("Circle with normal border"); 
    circle.draw(); 
    System.out.println("\nCircle of red border"); 
    redCircle.draw(); 
    System.out.println("\nRectangle of red border"); 
    redRectangle.draw(); 
    } 
}

步骤 6
执行程序,输出结果:
Circle with normal borderShape: Circle
Circle of red borderShape: CircleBorder Color: Red
Rectangle of red borderShape: RectangleBorder Color: Red

策略模式
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

1).传统实现方式

代码

public Double calculationPrice(String type, Double originalPrice, int n) {

    //中级会员计费
    if (type.equals("intermediateMember")) {
        return originalPrice * n - originalPrice * 0.1;
    }
    //高级会员计费
    if (type.equals("advancePrimaryMember")) {
        return originalPrice * n - originalPrice * 0.2;
    }else{
    //普通会员计费
    return originalPrice* n;
    }
}

传统的实现方式,通过传统if代码判断。这样就会导致后期的维护性非常差。当后期需要新增计费方式,还需要在这里再加上if(),也不符合设计模式的开闭原则。

2).策略模式实现

抽象类策略

package StrategyExercise;

public interface MemberStrategy {
    // 一个计算价格的抽象方法
    //price商品的价格 n商品的个数
    public double calcPrice(double price, int n);
}

具体实现类

// 普通会员——不打折
public class PrimaryMemberStrategy implements MemberStrategy { // 实现策略
    //重写策略方法具体实现功能
    @Override
    public double calcPrice(double price, int n) {
        return price * n;
    }
}
// 中级会员 打百分之10的折扣
public class IntermediateMemberStrategy implements MemberStrategy{
    @Override
    public double calcPrice(double price, int n) {
        double money = (price * n) - price * n * 0.1;
        return money;
    }
}
// 高级会员类 20%折扣
public class AdvanceMemberStrategy implements MemberStrategy{
    @Override
    public double calcPrice(double price, int n) {
        double money = price * n - price * n * 0.2;
        return money;
    }
}

上下文类

也叫做上下文类或环境类,起承上启下封装作用。

package StrategyExercise;

/**
 * 负责和具体的策略类交互
 * 这样的话,具体的算法和直接的客户端调用分离了,使得算法可以独立于客
* 端独立的变化。
 */

//上下文类/环境类
public class MemberContext {
    // 用户折扣策略接口
    private MemberStrategy memberStrategy;

    // 注入构造方法
    public MemberContext(MemberStrategy memberStrategy) {
        this.memberStrategy = memberStrategy;
    }

    // 计算价格
    public double qoutePrice(double goodsPrice, int n){
        // 通过接口变量调用对应的具体策略
        return memberStrategy.calcPrice(goodsPrice, n);
    }

}

测试类
package StrategyExercise;

// 测试类
public class Application {
    public static void main(String[] args) {
     // 具体行为策略
      MemberStrategy primaryMemberStrategy = new PrimaryMemberStrategy(); // 接口回调(向上转型)
        MemberStrategy intermediateMemberStrategy = new IntermediateMemberStrategy();
        MemberStrategy advanceMemberStrategy = new AdvanceMemberStrategy();

        // 用户选择不同策略
        MemberContext primaryContext = new MemberContext(primaryMemberStrategy);
        MemberContext intermediateContext = new MemberContext(intermediateMemberStrategy);
        MemberContext advanceContext = new MemberContext(advanceMemberStrategy);

        //计算一本300块钱的书
        System.out.println("普通会员的价格:"+ primaryContext.qoutePrice(300,1));// 普通会员:300
        System.out.println("中级会员的价格:"+ intermediateContext.qoutePrice(300,1));// 中级会员 270
        System.out.println("高级会员的价格:"+ advanceContext.qoutePrice(300,1));// 高级会员240
    }
}

运行结果

普通会员的价格:300.0

中级会员的价格:270.0

高级会员的价格:240.0

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

观察者模式

一:观察者模式

1.什么是观察者模式?

观察者模式是一种行为设计模式, 允许你定义一种订阅机制, 可在对象事件发生时通知多个“观察”该对象的其他对象。

观察者模式的基本介绍

又被称为发布-订阅(Publish/Subscribe)模式,它定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态变化时,会通知所有的观察者对象,使他们能够自动更新自己。

2.观察者模式适合应用场景

1.当一个对象状态的改变需要改变其他对象,或实际对象是事先未知的或动态变化的时,可使用观察者模式。

2.当应用中的一些对象必须观察其他对象时,可使用该模式。但仅能在有限时间内或特定情况下使用。

1.观察者模式优缺点

优点:

1.开闭原则。

无需修改发布者代码就能引入新的订阅者类 (如果是发布者接口则可轻松引入发布者类)。

2.可以在运行时建立对象之间的联系。

缺点:

1.订阅者的通知顺序是随机的。

4.观察者模式角色:

Subject:抽象主题(抽象被观察者),抽象主题角色把所有观察者对象保存在一个集合里,每个主题都可以有任意数量的观察者,抽象主题提供一个接口,可以增加和删除观察者对象。

ConcreteSubject:具体主题(具体被观察者),该角色将有关状态存入具体观察者对象,在具体主题的内部状态发生改变时,给所有注册过的观察者发送通知。

Observer:抽象观察者,是观察者的抽象类,它定义了一个更新接口,使得在得到主题更改通知时更新自己。

ConcrereObserver:具体观察者,实现抽象观察者定义的更新接口,以便在得到主题更改通知时更新自身的状态。

二:观察者模式代码实现

抽象被观察者

//(抽象被观察者)接口, 让(具体观察者)WeatherData 来实现
public interface Subject {
	public void registerObserver(Observer o);
	public void removeObserver(Observer o);
	public void notifyObservers();
}

具体被观察者

/**
  * 类是核心
  * 1. 包含最新的天气情况信息 
  * 2. 含有 观察者集合,使用ArrayList管理
* 3. 当数据有更新时,就主动的调用ArrayList,通知所有的(接入方)就看到
* 新的信息
  */
///具体被观察者
public class WeatherData implements Subject {
	//观察者集合
	private ArrayList<Observer> observers;
	//加入新的第三方
	public WeatherData() {
		observers = new ArrayList<Observer>();
	}
	private float temperatrue;
	private float pressure;
	private float humidity;

	public float getTemperature() {
		return temperatrue;
	}

	public float getPressure() {
		return pressure;
	}

	public float getHumidity() {
		return humidity;
	}

	//当数据有更新时,就调用setData
	public void setData(float temperature, float pressure, float humidity) {
		this.temperatrue = temperature;
		this.pressure = pressure;
		this.humidity = humidity;
		//调用dataChange,将最新的信息推送给接入方currentConditions
		dataChange();
	}

	public void dataChange() {
		//调用接入方的update
		notifyObservers();
	}

	//注册一个观察者
	@Override
	public void registerObserver(Observer o) {
		observers.add(o);
	}

	//移除一个观察者
	@Override
	public void removeObserver(Observer o) {
		if(observers.contains(o)) {
			observers.remove(o);
		}
	}

	//遍历所有的观察者,并通知
	@Override
	public void notifyObservers() {
		for(int i = 0; i < observers.size(); i++) {
		  observers.get(i).update(this.temperatrue,this.pressure, this.humidity);
		}
	}
}

抽象观察者
//抽象观察者接口,由具体观察者来实现()
public interface Observer {
	public void update(float temperature, float pressure, float humidity);
}

具体观察者
//具体观察者
public class BaiduSite implements Observer {

	// 温度,气压,湿度
	private float temperature;
	private float pressure;
	private float humidity;

	// 更新 天气情况,是由 WeatherData 来调用,我使用推送模式
	public void update(float temperature, float pressure, float humidity) {
		this.temperature = temperature;
		this.pressure = pressure;
		this.humidity = humidity;
		display();
	}

	// 显示
	public void display() {
		System.out.println("===百度网站====");
		System.out.println("***百度网站 气温 : " + temperature + "***");
		System.out.println("***百度网站 气压: " + pressure + "***");
		System.out.println("***百度网站 湿度: " + humidity + "***");
	}

}

//具体观察者
public class CurrentConditions implements Observer {

	// 温度,气压,湿度
	private float temperature;
	private float pressure;
	private float humidity;

	// 更新 天气情况,是由 WeatherData 来调用,我使用推送模式
	public void update(float temperature, float pressure, float humidity) {
		this.temperature = temperature;
		this.pressure = pressure;
		this.humidity = humidity;
		display();
	}

	// 显示
	public void display() {
		System.out.println("***Today mTemperature: " + temperature + "***");
		System.out.println("***Today mPressure: " + pressure + "***");
		System.out.println("***Today mHumidity: " + humidity + "***");
	}
}

测试

	public static void main(String[] args) {

		//创建一个WeatherData
		WeatherData weatherData = new WeatherData();
		
		//创建观察者
		CurrentConditions currentConditions = new CurrentConditions();
		BaiduSite baiduSite = new BaiduSite();
		
		//注册到weatherData
		weatherData.registerObserver(currentConditions);
		weatherData.registerObserver(baiduSite);
		
		//测试
		System.out.println("通知各个注册的观察者,看看信息");
		weatherData.setData(10f, 100f, 30.3f);
		
		weatherData.removeObserver(currentConditions);
		//测试
		System.out.println();
		System.out.println("通知各个注册的观察者,看看信息");
		weatherData.setData(10f, 100f, 30.3f);

	}

运行结果:

在这里插入图片描述
代理模式
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

// 委托接口
public interface IHelloService {

    /**
     * 定义接口方法
     * @param userName
     * @return
     */
    String sayHello(String userName);

}
// 委托类实现
public class HelloService implements IHelloService {

    @Override
    public String sayHello(String userName) {
        System.out.println("helloService" + userName);
        return "HelloService" + userName;
    }
}

// 代理类
public class StaticProxyHello implements IHelloService {

    private IHelloService helloService = new HelloService();

    @Override
    public String sayHello(String userName) {
        /** 代理对象可以在此处包装一下*/
        System.out.println("代理对象包装礼盒...");
        return helloService.sayHello(userName);
    }
}
// 测试静态代理类
public class MainStatic {
    public static void main(String[] args) {
        StaticProxyHello staticProxyHello = new StaticProxyHello();
        staticProxyHello.sayHello("isole");
    }
}

在这里插入图片描述

public interface InvocationHandler {
    /**
     * 调用处理
     * @param proxy 代理类对象
     * @param methon 标识具体调用的是代理类的哪个方法
     * @param args 代理类方法的参数
     */
    public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable;
}

Demo 如下:
// 委托类接口
public interface IHelloService {
    /**
     * 方法1
     * @param userName
     * @return
     */
    String sayHello(String userName);

    /**
     * 方法2
     * @param userName
     * @return
     */
    String sayByeBye(String userName);
    }

// 委托类
public class HelloService implements IHelloService {
    @Override
    public String sayHello(String userName) {
        System.out.println(userName + " hello");
        return userName + " hello";
    }

    @Override
    public String sayByeBye(String userName) {
        System.out.println(userName + " ByeBye");
        return userName + " ByeBye";
    }
}

// 中间类
public class JavaProxyInvocationHandler implements InvocationHandler {
    /**
     * 中间类持有委托类对象的引用,这里会构成一种静态代理关系
     */
    private Object obj ;

    /**
     * 有参构造器,传入委托类的对象
     * @param obj 委托类的对象
     */
    public JavaProxyInvocationHandler(Object obj){
        this.obj = obj;
    }

    /**
     * 动态生成代理类对象,Proxy.newProxyInstance
     * @return 返回代理类的实例
     */
    public Object newProxyInstance() {
        return Proxy.newProxyInstance(
                //指定代理对象的类加载器
                obj.getClass().getClassLoader(),
              //代理对象需要实现的接口,可以同时指定多个接口
              obj.getClass().getInterfaces(),
              //方法调用的实际处理者,代理对象的方法调用都会转发到这里
              this);
    }

    /**
     *
     * @param proxy 代理对象
     * @param method 代理方法
     * @param args 方法的参数
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("invoke before");
        Object result = method.invoke(obj, args);
        System.out.println("invoke after");
        return result;
    }
}

// 测试动态代理类
public class MainJavaProxy {
    public static void main(String[] args) {
    JavaProxyInvocationHandler proxyInvocationHandler = new JavaProxyInvocationHandler(new HelloService());
        IHelloService helloService = (IHelloService) proxyInvocationHandler.newProxyInstance();
        helloService.sayByeBye("paopao");
        helloService.sayHello("yupao");
    }
}

在这里插入图片描述

public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h) 
throws IllegalArgumentException
    {
        // 检查空指针
        Objects.requireNonNull(h);
        // 用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象
        final Class<?>[] intfs = interfaces.clone();
        // 获取系统的安全接口,不为空的话需要验证是否允许访问这种关系的代理访问
        final SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
        }
        /*
         * 生成代理类 Class,通过类加载器和接口
         */
        Class<?> cl = getProxyClass0(loader, intfs);
        /*
         * 通过构造器来创建实例
         */
        try {
            if (sm != null) {
                checkNewProxyPermission(Reflection.getCallerClass(), cl);
            }
            //获取所有的构造器
            final Constructor<?> cons = cl.getConstructor(constructorParams);
            final InvocationHandler ih = h;
            // 构造器不是public的话需要设置可以访问
            if (!Modifier.isPublic(cl.getModifiers())) {
                AccessController.doPrivileged(new PrivilegedAction<Void>() {
                    public Void run() {
                        cons.setAccessible(true);
                        return null;
                    }
                });
            }
            // 返回创建的代理类Class的实例对象
            return cons.newInstance(new Object[]{h});
        } catch (IllegalAccessException|InstantiationException e) {
            throw new InternalError(e.toString(), e);
        } catch (InvocationTargetException e) {
            Throwable t = e.getCause();
            if (t instanceof RuntimeException) {
                throw (RuntimeException) t;
            } else {
                throw new InternalError(t.toString(), t);
            }
        } catch (NoSuchMethodException e) {
            throw new InternalError(e.toString(), e);
        }
}

在这里插入图片描述

<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>2.2</version>
</dependency>

在这里插入图片描述

// 委托类,是一个简单类
public class CglibHelloClass {
   /**
    * 方法1
    * @param userName
    * @return
    */
   public String sayHello(String userName){
       System.out.println("目标对象的方法执行了");
       return userName + " sayHello";
   }

   public String sayByeBye(String userName){
       System.out.println("目标对象的方法执行了");
       return userName + " sayByeBye";
   }

}
/**
* CglibInterceptor 用于对方法调用拦截以及回调
*
*/
public class CglibInterceptor implements MethodInterceptor {
   /**
    * CGLIB增强类对象,代理类对象是由Enhancer类创建的,
    * Enhancer是CGLIB的字节码增强器,可以很方便的对类进行拓展
    */
   private   Enhancer  enhancer  =  new Enhancer();
   /**
    * @param obj  被代理的对象
    * @param method 代理的方法
    * @param args 方法的参数
    * @param proxy CGLIB方法代理对象
    * @return cglib生成用来代替Method对象的一个对象,使用MethodProxy比调用JDK
*自身的Method直接执行方法效率会有提升
    * @throws Throwable
    */
   @Override
   public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
       System.out.println("方法调用之前");
       Object o = proxy.invokeSuper(obj, args);
       System.out.println("方法调用之后");
       return o;
   }

   /**
    * 使用动态代理创建一个代理对象
    * @param c
    * @return
    */
   public  Object  newProxyInstance(Class<?> c) {
       /**
        * 设置产生的代理对象的父类,增强类型
        */
       enhancer.setSuperclass(c);
       /**
        * 定义代理逻辑对象为当前对象,要求当前对象实现 MethodInterceptor 接口
        */
       enhancer.setCallback(this);
       /**
        * 使用默认无参数的构造函数创建目标对象,这是一个前提,被代理的类要提供无参构造方法
        */
       return enhancer.create();
   }
}


//测试类
public class MainCglibProxy {
   public static void main(String[] args) {
       CglibProxy cglibProxy = new CglibProxy();
       CglibHelloClass cglibHelloClass = (CglibHelloClass) cglibProxy.newProxyInstance(CglibHelloClass.class);
       cglibHelloClass .sayHello("isole");
       cglibHelloClass .sayByeBye("sss");
   }
}

在这里插入图片描述

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐