IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> 23种设计模式——不断更新中 -> 正文阅读

[Java知识库]23种设计模式——不断更新中

1.单例模式

  • 在大部分时候,我们把类的构造器定义成public访问权限,允许任何类自由创建该类的对象。但在某些时候,允许其他类自由创建该类的对象没有任何意义,还可能造成系统性能下降(因为创建一个对象的系统开销问题)。例如系统可能只有一个窗口管理器,一个假脱机打印设备或一个数据库引擎访问点,此时如果在系统中为这些类创建多个对象就没有太大的实际意义。
  • 如果一个类始终只能创建一个实例,则这个类被称为单例类。
  • 总之,在一些特殊场景下,要求不允许自由创建该类的对象,而是只允许为该类创建一个对象。为了避免其他类自由创建该类的实例,我们把该类的构造器使用private修饰,从而把该类的所有构造器隐藏起来。
  • 根据良好封装的原则:一旦把该类的构造器隐藏起来,则需要提供一个public方法作为该类的访问点,用于创建该类的对象,且该方法必须使用 static修饰(因为调用该方法之前还不存在对象,因此调用该方法的不可能是对象,只能是类)。
  • 除此之外,该类还必须缓存已经创建的对象,否则该类无法知道是否曾经创建过对象,也就无法保证只创建一个对象。为此该类需要使用一个属性来保存曾经创建的对象,因为该属性需要被上面的静态方法访问,故该属性必须使用static修饰。
    基于上面的介绍,下面的程序创建了一个单例类:
class Singleton{
    //使用一个变量来缓存曾经创建的对象,要被getInstance访问,所以static
    private static Singleton instance;
    //private私有化无参构造
    private Singleton(){}
    //提供外界接口,静态类方法,类加载的时候调用,可以通过类名直接调用
    //调用方法前不存在对象,因此调用方法时不可能是类,只能是对象,所以要加static修饰
    public static Singleton getInstance(){
        if(instance==null)instance=new Singleton();
        return instance;
    }
}
public class Main {
    public static void main(String[] args){
        //测试 instance1是直接创建的,instance2是返回之前创建的
        Singleton instance1 = Singleton.getInstance();
        Singleton instance2 = Singleton.getInstance();
        //返回true
        System.out.println(instance1==instance2);
    }
}

正是通过上面getInstance方法提供的自定义控制(这也是封装的优势:不允许自由访问类的属性和实现细节,而是通过方法来控制合适暴露),保证Singleton类只能产生一个实例,所以在TestSingleton类的 main方法中看到两次产生的Singleton对象实际上是同一个对象。

2. 模板模式

  • 抽象类体现的就是一种模板模式的设计,抽象类作为多个子类的通用模板,子类在抽象类的基础上进行扩展、改造,但子类总体上会大致保留抽象类的行为方式。
  • 如果编写一个抽象父类,父类提供了多个子类的通用方法,并把一个或多个方法留给其子类实现,这就是一种模板模式,模板模式也是最常见、最简单的设计模式之一。例如前面介绍的Shape、Circle和Triangle 三个类,已经使用了模板模式。下面再介绍一个模板模式的范例,在这个范例的抽象父类中,父类的普通方法依赖于一个抽象方法,而抽象方法则推迟到子类中提供实现。
//Mian函数可以当作计算具体一种汽车的转速(假如Main是小轿车的意思)
public class Main extends SpeedMeter{
    //在小轿车的类里面重写定义它的半径
    @Override
    public double getRadius() {
        return 0.28;
    }
    public static void main(String[] args){
        Main csm = new Main();
        //设置转速
        csm.setTurnRate(15);
        System.out.println(csm.getSpeed());

    }
}
//这个是抽象的父类
abstract class SpeedMeter{
    //转速
    private double turnRate;
    public SpeedMeter(){}
    //把返回车轮半径的方法定义成抽象方法
    public abstract double getRadius();
    public void setTurnRate(double turnRate){
        this.turnRate=turnRate;
    }
    //定义计算速度的通用算法
    public double getSpeed(){
        //速度等于 车轮半径*2*PI*转速
        return Math.PI*2*getRadius()*turnRate;
    }
}
  • 上面程序种SpeedMeter类定义了一个抽象的SpeedMeter类(车速表),该表里定义了一个getSpeed方法,该方法用于返回当前车速,getSpeed方法依赖于getRadius方法的返回值。对于一个抽象的SpeedMeter类而言,它无法确定车轮的半径,因此getRadius方法必须推迟到其子类中实现。
  • 上面程序中其子类Main的代码,该类实现了其抽象父类的getRadius()方法,即可创建SpeedMeter类的对象,也可通过该对象来取得当前速度。
  • SpeedMeter类里提供了速度表通用算法,但一些具体的实现细节则推迟到其子类Main类中实现。这也是一种典型的模板模式。
  • 模板模式在面向对象的软件中很常用。其原理简单,实现也很简单。下面是使用模板模式的一些简单规则:
  1. 抽象父类可以只定义需要使用的某些方法,其余则留给其子类实现。
  2. 父类中可能包含需要调用的其他系列方法的方法,这些被调方法既可以由父类实现,也可以由其子类实现。父类里提供的方法只是定义了一个通用算法,其实现也许并不完全由自身实现,而必须依赖于其子类的辅助。

3.命令模式

  • 考虑这样一种场景:某个方法需要完成某一个行为,但这个行为的具体实现无法确定,必须等到执行该方法时才可以确定。具体一点:假设有个方法需要遍历某个数组的数组元素,但无法确定在遍历数组元素时如何处理这些元素,需要在调用该方法时指定具体的处理行为。
  • 这个要求看起来有点奇怪:这个方法不仅需要普通数据可以变化,甚至还有方法执行体也需要变化,难道我们能把“处理行为”作为一个参数传入该方法?
  • 对于这样一个需求,我们必须把“处理行为”作为参数传入该方法,这个“处理行为”用编程来实现就是一段代码。那如何把这段代码传入该方法呢?
  • 因为Java不允许代码块单独存在,因此我们使用一个Command 接口来定义一个方法,用这个方法来封装“处理行为”。下面是该Command接口代码。
public interface Commond{
    //接口里面定义process方法用于封装"处理行为"
    void process(int[] target);
}
  • 上面的 Command 接口里定义了一个process方法,这个方法用于封装“处理行为”,但这个方法没有方法体——因为现在还无法确定这个处理行为。
  • 下面是需要处理数组的处理类,在这个处理类中包含一个process方法,这个方法无法确定处理数组的处理行为,所以定义该方法时使用了一个 Command参数,这个Command参数负责对数组的处理行为。该类的程序代码如下。
public class ProcessArray{
    public void process(int[] target,Commond cmd){
        cmd.process(target);
    }
}

通过一个Command类,就实现了让 ProcessArray类和具体“处理行为”的分离,程序使用Command接口代表了对数组的处理行为。Command接口也没有提供真正的处理,只有等到需要调用ProcessArray对象的process方法时,才真正传入一个 Command对象,才确定对数组的处理行为。

public class TestCommand{
    public static void main(String[] args) {
        ProcessArray pa = new ProcessArray();
        int[] target={-3,-4,5,6};
        //第一次处理数组,具体的处理行为取决于PrintCommand
        pa.process(target,new PrintCommand());
        //第二次处理数组,具体的处理行为取决于AddCommand
        pa.process(target,new AddCommand());
    }
}
  • 两次不同处理行为的效果,也就实现了process方法和“处理行为”的分离,两次不同的处理行为是通过 PrintCommand类和 AddCommand类提供的。下面分别是 PrintCommand类和AddCommand类的代码。
public class PrintCommand implements Command{
    public void process(int[] target) {
        for(int tmp:target){
            System.out.println("迭代输出目标数组的元素"+tmp);
        }
    }
}
public class AddCommand implements Command{
    public void process(int[] target) {
        int sum=0;
        for(int tmp:target){
        sum+=tmp;
        }
        System.out.println("数组的元素总和是:"+sum);
    }
}

对于PrintCommand和 AddCommand两个实现类而言,实际有意义的部分就是process(int[] target)方法,该方法的方法体就是传入ProcessArray类里 process方法的“处理行为”,通过这种方式就可实现process方法和“处理行为”的分离。

4. 简单工厂模式(可以看DateFormate源码就是使用的这种模式)

  1. 定义:定义一个工厂类,他可以根据参数的不同返回不同类的实例,被创建的实例通常都具有共同的父类
  2. 在简单工厂模式中用于被创建实例的方法通常为静态(static)方法,因此简单工厂模式又被成为静态工厂方法(Static Factory Method),属于类的创建型模式
  3. 需要什么,只需要传入一个正确的参数,就可以获取所需要的对象,而无需知道其实现过程
  4. 例如,我开一家披萨店,当客户需要某种披萨并且我这家店里也能做的时候,我就会为其提供所需要的披萨(当然是要钱的哈哈),如果其所需的我这没有,则是另外的情况,后面会谈。这时候,我这家 披萨店就可以看做工厂(Factory),而生产出来的披萨被成为产品(Product),披萨的名称则被称为参数,工厂可以根据参数的不同返回不同的产品,这就是简单工厂模式
    在这里插入图片描述
  • 结构
  1. Factory(工厂):核心部分,负责实现创建所有产品的内部逻辑,工厂类可以被外界直接调用,创建所需对象
  2. Product(抽象类产品):工厂类所创建的所有对象的父类,封装了产品对象的公共方法,所有的具体产品为其子类对象
  3. ConcreteProduct(具体产品):简单工厂模式的创建目标,所有被创建的对象都是某个具体类的实例。它要实现抽象产品中声明的抽象方法(有关抽象类)
  • 实现
public class Main{
    public static void main(String[] args) {
        //测试
        Product product=Factory.GetProduct("A");
        product.MethodDiff();
        Product product0=Factory.GetProduct("B");
        product0.MethodDiff();
    }
}

//定义一个工厂类
class Factory{
    public static Product GetProduct(String arg){
        Product product=null;
        if(arg.equals("A")){
            product = new ProductA();
        }else {
            product = new ProductB();
        }
        return product;
    }
}


//总的公共的抽象类
abstract class Product{
    public void MethodName(){
        System.out.println("我是一个产品");
        //公共方法实现
    }
    //声明抽象的业务方法
    public abstract void MethodDiff();
}
class ProductA extends Product{
    @Override
    public void MethodDiff() {
        System.out.println("产品A");
    }
}
class ProductB extends Product{

    @Override
    public void MethodDiff() {
        System.out.println("产品B");
    }
}
  • 优点:实现对象的创建和使用分离,创建交给工厂去负责,不关心怎么创建,只关心怎么使用
  • 缺点:工厂类不够灵活,新增一个就要修改工厂类,修改判断逻辑,产品很多的话,逻辑将会非常的复杂
  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-03-08 22:14:58  更:2022-03-08 22:16:18 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 10:34:26-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码