栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

java设计模式

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

java设计模式

设计模式是在实际开发过程中,为解决特定问题而提出的解决方案;是经过反复使用的,多人知晓的,经过分类编目的代码设计经验总结;通过使用这些解决方案,可以提高程序的可扩展性,可重用性,以及灵活性。

目录

 1 策略模式

2 装饰模式

3 观察者模式(订阅发布模式)

4 单例模式

单线程

多线程

5 工厂模式

6 适配器模式

7 代理模式

8 模板方法模式

9 责任链模式

 1 策略模式

从一个计算器例子来看:  

//计算器操作接口
public interface Operation {

    public int doOperation(int num1,int num2);
}
//计算器类
//策略模式
public class Calculator {
    private Operation operation;

    public void setOperation(Operation operation){
        this.operation = operation;
    }

    public int doOperation(int num1, int num2){
        return operation.doOperation(num1,num2);
    }
}
//计算器操作接口实现类
public class OperationAdd implements Operation {

    @Override
    public int doOperation(int num1, int num2) {
        return num1 + num2;
    }
}
public class Test {

    public static void main(String[] args) {

      Calculator calculator = new Calculator();

      calculator.setOperation(new OperationAdd());
      System.out.println(calculator.doOperation(5, 6));  

      calculator.setOperation(new OperationSub());
      System.out.println(calculator.doOperation(2, 5));
      
      calculator.setOperation((a,b)->a*b);
      System.out.println(calculator.doOperation(10, 20));

      calculator.setOperation((int a, int b)->{
        return a/b;
      });
      System.out.println(calculator.doOperation(50, 5));
    }
}

显然一个计算器用策略模式实现起来感觉很麻烦,明明只需要一个计算类就可以实现计算器的功能,即:

public class Calculator {
    public int doOperation(int num1, int num2){
        return num1 + num2;
    }
}

虽然上述代码简单,但是它的扩展性极差,比如我们的计算器需要减法功能,只能修改上述代码,而策略模式只需要扩展代码即可;我们的设计模式大多数是用在Jar包中,供其他人使用的,所以代码需要极好的扩展性,上述代码不符合;而策略模式虽然看起来麻烦,但是放长远眼光看,它具有很好的扩展性。满足“开闭原则”(修改关闭,扩展开放)

2 装饰模式

用小美买衣服来举例。

装饰模式有两个角色:

  • 装饰器:衣服

  • 被装饰的对象:小美

装饰模式有两个特点:

  • 两个角色实现同一个接口

  • 装饰器当中使用被装饰的对象

如果不使用装饰模式如下代码所示,虽然简单,但没有扩展性,小美买衣服必须的修改里面的代码

public class Test {
    public void show(){
        System.out.println("买了一件衬衣,累计消费100.0元");
        System.out.println("买了一件裤子,累计消费300.0元");
    }
}

使用装饰模式代码如下:

//定义一个Person接口
public interface Person {
    public Double cost();
    public void show();
}


//小美实现了Person接口
public class XiaoMei implements Person{
    @Override
    public Double cost() {
        return 0.0;
    }

    @Override
    public void show() {
        System.out.println("没穿衣服的小美");
    }
}


//定义一个衣服装饰器的抽象类(为了方便拓展,所以定义为抽象类),让其实现Person接口
//需要注意的是,抽象类实现接口,不需要重写接口里面的方法
public abstract class ClothesDecorate implements Person{

    protected Person person;

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


//装饰器子类衬衣Shirt类,继承了衣服装饰器,并重写里面的方法
//需要注意的是必须要给Shirt类定义一个有参构造函数,原理如下:
//首先构造函数无法继承,其次实例化子类对象时是从超类开始实例化的
//故超类中没有无参构造,子类也就无法使用无参构造实例化对象;
//构造函数无法继承,子类不写构造时,是默认是使用无参构造,所以系统会提示必须手动写一个有参构造
//,有参构造里面参数必须和超类一致,参数数量可以不和超类保持一致,
public class Shirt extends ClothesDecorate{

    public Shirt(Person person) {
        super(person);
    }

    @Override
    public Double cost() {
        return this.person.cost()+100;
    }

    @Override
    public void show() {
         this.person.show();
        System.out.println("买了一件衬衣,累计消费"+this.cost());
    }
}


//装饰器子类裤子Trouser类,继承了衣服装饰器,并重写里面的方法
public class Trouser extends ClothesDecorate{

    public Trouser(Person person) {
        super(person);
    }

    @Override
    public Double cost() {
        return this.person.cost()+200;
    }

    @Override
    public void show() {
        this.person.show();
        System.out.println("买了一条裤子,累计消费"+this.cost());
    }
}


//测试类
public class Test {
    public static void main(String[] args) {
        Person xiaoMei = new XiaoMei();
        xiaoMei = new Shirt(xiaoMei);
        xiaoMei = new Trouser(xiaoMei);
        xiaoMei.show();
    }
}

//打印结果如下:
没穿衣服的小美
买了一件衬衣,累计消费100.0
买了一条裤子,累计消费300.0

Process finished with exit code 0

3 观察者模式(订阅发布模式)

定义了对象之间一对多的依赖关系,当这个对象状态发生改变的时候,它的所有观察者都会收到通知,并自动更新。

区分观察者与被观察者:收到通知的对象即是观察者。

包含两个主角:

  • 观察者(订阅者)

  • 被观察者(发布者)

以客户订阅报纸为例:

//定义顾客抽象类,并定义了一个更新的抽象方法
public abstract class Customer {
    public abstract void update();
}


//定义了一个继承了顾客抽象类的顾客A类,并重写了更新的方法
public class CustomerA extends Customer{
    @Override
    public void update() {
        System.out.println("客户A的报纸已送达");
    }
}


//定义了一个继承了顾客抽象类的顾客B类,并重写了更新的方法
public class CustomerB extends Customer{
    @Override
    public void update() {
        System.out.println("客户B的报纸已送达");
    }
}


//定义了报社类,注入一个泛型是顾客类的集合变量,形成一对多的依赖关系
//定义了一个往集合中添加顾客的方法
//定义了一个通知所有顾客的方法,其中遍历顾客集合,次第调用每个用户的更新方法
//最后定义了一个报社状态发生改变的方法,并在其中调用报社通知所有顾客的方法
//即观察者模式:当报纸状态发生改变时,客户都会收到通知,并让客户更新
import java.util.ArrayList;
import java.util.List;

public class NewspaperOffice {
    private List customers = new ArrayList<>();

    public void addCustomer(Customer customer){
        customers.add(customer);
    }

    public void notifyAllObservers(){
        for (Customer customer : customers) {
            customer.update();
        }
    }

    public void newspaper(){
        System.out.println("第二天的报纸已经备妥");
        this.notifyAllObservers();
    }
}


//测试类
public class Test {
    public static void main(String[] args) {
        NewspaperOffice newspaperOffice = new NewspaperOffice();
        Customer customerA = new CustomerA();
        Customer customerB = new CustomerB();
        newspaperOffice.addCustomer(customerA);
        newspaperOffice.addCustomer(customerB);
        newspaperOffice.newspaper();
    }
}
//打印结果如下:
第二天的报纸已经备妥
客户A的报纸已送达
客户B的报纸已送达

Process finished with exit code 0

4 单例模式

什么是单例模式:一个类只存在一个实例对象

单线程
public class Single {
    private static Single instance;
    private Single(){
        System.out.println("创建了single对象");
    };

    public static Single getetInstance() {
       if (instance == null) instance = new Single();
       return instance;
    }
}

public class Test {
    public static void main(String[] args) {
        for (int i = 0; i<10; i++){
            Single.getetInstance();
        }
    }
}
//程序运行结果
创建了single对象

上述代码实现了单线程下的单例模式。

可以看出它具备如下特点:

  • 类的构造方法使用private修饰,声明为私有,如此就无法在类的外部使用new关键字创建对象了

  • 在该类内部创建一个该类的实例对象,并让静态变量instance引用该对象,由于静态变量禁止外界访问,因此使用private修饰,声明为私有

  • 为了让类的外部能够获得类的实例对象,需要定义一个静态方法getInstance(),用于返回该类实例instance,该方法是静态的,外界可以通过“类名+方法名”的方式访问

多线程
public class Test {
    public static void main(String[] args) {
        for (int i = 0; i<5; i++){
            new Thread(()->{
                Single.getetInstance();
            }).start();
        }
    }
}
//程序运行结果
创建了single对象
创建了single对象
创建了single对象
创建了single对象
创建了single对象

可以看出在多线程下,Single类已经不是单例模式

原因如下:当第一个线程进入getetInstance()方法,if判断instance 为空,正要创建对象时;第二个线程开始执行,进入getetInstance()方法,由于第一个线程未创建对象,instance 还是空,所以也要执行if判断之后的语句,创建对象,正要开始创建时;此刻第三个线程开始执行.....以此类推

public class Single {
    private static Single instance;
    private Single(){
        System.out.println("创建了single对象");
    };
//线程同步方法
    public static synchronized Single getetInstance() {
       if (instance == null) instance = new Single();
       return instance;
    }
}


public class Test {
    public static void main(String[] args) {
        for (int i = 0; i<5; i++){
            new Thread(()->{
                Single.getetInstance();
            }).start();
        }
    }
}
//程序执行结果
创建了single对象

由此看出该Single类实现了多线程下的单例模式

但是上述该Single类并不是解决多线程下单例模式的完美方案,因为每上一次锁,都会有性能损耗,而该Single类五个线程上了五次锁

我们清楚上述single类,在调用方法的时候,才会创建对象,即:延时加载

我们可以设计成在类加载的时候就创建对象,由此避免了多线程下多次调用getetInstance()方法,多次创建对象的问题

public class Single {
    private static Single instance = new Single();
    private Single(){
        System.out.println("创建了single对象");
    };

    public static Single getetInstance() {
       return instance;
    }
}


public class Test {
    public static void main(String[] args) {
        for (int i = 0; i<5; i++){
            new Thread(()->{
                Single.getetInstance();
            }).start();
        }
    }
}
//程序运行结果
创建了single对象

除了上述方案实现单例模式外,还有一种方式实现单例模式,即:双重检测

这种方式主要用到了两个关键字:

  • volatile:保证变量对所有线程都是可见的,即:一个线程修改变量后,所有的线程都可以看见,并作出响应,如此会减少了加锁带来性能损耗的问题,详解见:【Java面试】volatile 关键字到底是什么? - 知乎 (zhihu.com)

  • synchronized:线程同步机制,详解见:同步代码块synchronized(对象)醉梦依依惜的博客-CSDN博客synchronized撖寡情

public class Single {
    private volatile static Single instance ;
    private Single(){
        System.out.println("创建了single对象");
    };

    public static Single getetInstance() {
        if (instance == null){
            synchronized (Single.class){
                if (instance == null) {
                    instance = new Single();
                }
            }
        }
       return instance;
    }
}

public class Test {
    public static void main(String[] args) {
        for (int i = 0; i<5; i++){
            new Thread(()->{
                Single.getetInstance();
            }).start();
        }
    }
}
//程序运行结果
创建了single对象

上述双重检测的方案,也实现了多线程下的单例模式

5 工厂模式

不需要开发者手动创建对象,而是提供了一个容器,容器里面加载了对象,开发者需要使用对象的时候,只需要从容器中获取即可

//定义一个电脑的接口
public interface Computer {
}

//A型号的电脑
public class ComputerA implements Computer{
}

//B型号的电脑
public class ComputerB implements Computer{
}


//电脑工厂,按顾客需求创建A型号电脑或B型号电脑
public class ComputerFactory {
    public Computer createComputer(String name){
        Computer computer = null;
        if (name.equals("a")) computer = new ComputerA();
        if (name.equals("b")) computer = new ComputerB();
        return computer;
    }
}

//测试类,顾客需要A型号的电脑,则调用工厂对象方法,传入电脑型号A即可
public class Test {
    public static void main(String[] args) {
        ComputerFactory  computerFactory = new ComputerFactory();
        Computer a = computerFactory.createComputer("a");
        Computer b = computerFactory.createComputer("b");
    }
}

6 适配器模式

将一个接口转换成用户希望的另一个接口,解决了类之间接口不兼容的问题

模拟场景:音乐播放器只支持wma格式,利用适配器模式,让它支持MP3格式

//只支持wma格式的播放器
public class PlayerWma {

    public void play(String filename){
        System.out.println("play Wma:" + filename);
    }
}

//定义一个万能播放器的接口
public interface Player {
    public void play(String filename);
}

//适配器实现了万能播放器的接口,把只支持播放wma格式的播放器插入到适配器中,则可支持播放所有格式的音乐
public class PlayAdapter implements Player{

    private PlayerWma Player;

    public PlayAdapter(PlayerWma player){
        this.Player = player;
    }
    @Override
    public void play(String filename) {
        System.out.println("play Mp3:" + filename);
    }
}

//测试类
public class Test {
    public static void main(String[] args) {
        PlayAdapter playAdapter = new PlayAdapter(new PlayerWma());
        playAdapter.play("xxx.mp3");
    }
}
//程序运行结果
play Mp3:xxx.mp3

可以看出,在适配器下模式下,输入MP3格式文件,播放器可以播放

7 代理模式

通过代理模式可以实现目标对象和非业务方法的解耦合,从而使业务代码和非业务代码分离

模拟场景:小明通过委托租房中介来租房子

//定义一个房子的接口
public interface House {
    public void findHouse();
}

//小明要找房子,实现房子接口
public class XiaoMing implements House{
    @Override
    public void findHouse() {
        //业务代码
        System.out.println("这是业务代码:小明要租房子");
    }
}

//小明通过房子中介来找房子
public class HouseProxy implements House{
    private House house;
    public HouseProxy(House house){
        this.house = house;
    }
    @Override
    public void findHouse() {
        house.findHouse();
        //非业务代码
        System.out.println("输出一些日志");
    }
}

public class Test {
    public static void main(String[] args) {
        HouseProxy houseProxy = new HouseProxy(new XiaoMing());
        houseProxy.findHouse();
    }
}

8 模板方法模式

它提供了一套算法的模板,把各个类中相同的业务代码都提取出来,封装到一个父类当中,由此形成了一套模板;这套模板在不同的子类当中可以进行复用,减少了代码量,同时子类在不改变算法的结构下,可以去重新定义算法中的某些步骤

模拟场景:炒菜的步骤

//定义一个炒菜步骤的模板类
public abstract class Cook {

    public void open(){
        System.out.println("打开油烟机");
    }

    public void fire(){
        System.out.println("开火");
    }

    public abstract void doCook();

    public void outFire(){
        System.out.println("关火");
    }

    public void close(){
        System.out.println("关闭油烟机");
    }
//步骤方法是固定的,不能改动,故要用final修饰
    public final void cook(){
        this.open();
        this.fire();
        this.doCook();
        this.outFire();
        this.close();
    }

}

//炒西红柿鸡蛋
public class CookTomato extends Cook {

    @Override
    public void doCook() {
        System.out.println("西红柿炒鸡蛋");
    }
}

//炒土豆丝
public class CookPotato extends Cook{

    @Override
    public void doCook() {
        System.out.println("炒土豆丝");
    }
}


public class Test {
    public static void main(String[] args) {
        Cook cook = new CookTomato();
        cook.cook();
        System.out.println("*************");
        cook = new CookPotato();
        cook.cook();
    }
}
//程序运行结果
打开油烟机
开火
西红柿炒鸡蛋
关火
关闭油烟机
*************
打开油烟机
开火
炒土豆丝
关火
关闭油烟机

模板方法模式具体在开发中的应用场景例如用servlet做web开发

即:自定义类——>继承HttpServlet——>继承AbstractServlet——>实现Servlet接口

9 责任链模式

顾名思义,采用链式调用的形式,责任指的是对请求的某些操作

当业务比较复杂的时候,采用分段的处理,每一段负责一个业务,整体形成一个调用链

具体来讲,就是为某一个请求创建一个对象链,每一个对象都会依次检查这个请求,对它进行一些处理,加入一些逻辑等,或者直接将它传给下一个对象,类似一层层的过滤操作

模拟场景:过滤审核论坛中用户发表帖子中的内容

//创捷一个待过滤的请求类
public class Post {
    private String content;

    public void setContent(String content) {
        this.content = content;
    }

    public String getContent() {
        return content;
    }
}

//对象链节点的抽象超类
public abstract class PostHandler {
    private PostHandler handler;

    public void setHandler(PostHandler handler) {
        this.handler = handler;
    }
    
    //节点过滤操作:每一个子类的该方法操作都不一样,故写成抽象方法
    public abstract void handlerRequest(Post post);
    
   //节点传递操作:调用下一个链节点的方法,不允许子类修改,故定义成final类型
    protected final void next(Post post){
        if (handler != null){
            this.handler.handlerRequest(post);
        }
    }
}

//对象链节点A
public class HandlerA extends PostHandler{
    @Override
    public void handlerRequest(Post post) {
        String content = post.getContent();
        content = content.replace("广告","**");
        post.setContent(content);
        System.out.println("过滤广告");
        next(post);
    }
}

//对象链节点B
public class HandlerB extends PostHandler{
    @Override
    public void handlerRequest(Post post) {
        String content = post.getContent();
        content = content.replace("游戏推广","**");
        post.setContent(content);
        System.out.println("过滤游戏推广");
        next(post);
    }
}

//测试类
public class Test {
    public static void main(String[] args) {
        PostHandler handlerA = new HandlerA();
        PostHandler handlerB = new HandlerB();
        handlerA.setHandler(handlerB);
        Post post = new Post();
        post.setContent("正常内容,广告,游戏推广");
        System.out.println("过滤前的内容:" + post.getContent());
        handlerA.handlerRequest(post);
        System.out.println("过滤后的内容:" + post.getContent());
    }
}
//程序运行结果
过滤前的内容:正常内容,广告,游戏推广
过滤广告
过滤游戏推广
过滤后的内容:正常内容,**,**


转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/1014812.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号