Java语言在运用规划形式时有很多可供挑选的形式,其间最常用的是23种规划形式。在本文中,咱们将对Java中的这23种规划形式进行扼要介绍。

  下面的三篇文章详细的介绍了Java 的23种规划形式的介绍和运用场景,包括详细的代码完成。

  • Java常用规划形式(一) – ()
  • Java常用规划形式(二) – ()
  • Java常用规划形式(三) – ()

Java的23种规划形式

一文总结Java的23种设计模式
  Java中的23种规划形式首要分为三类:

  • 创立型形式:首要处理目标创立问题
  • 结构型形式:首要处理目标组合问题
  • 行为型形式:首要处理目标之间的交互问题

创立型形式

  Java中的创立型规划形式首要用于目标的创立和组装。这些形式经过笼统化和解耦目标的创立进程,能够使体系愈加灵活和可扩展。下面是Java中的5种创立型规划形式:

  • 单例形式:确保一个类只要一个实例,并供给一个全局访问点。

  • 工厂形式:在不暴露创立目标的逻辑的前提下,运用工厂办法来创立目标。

  • 笼统工厂形式:供给一个接口,用于创立相关或依靠目标的系列,而不需求指定实践完成类。

  • 制作者形式:将杂乱目标的构建与表明别离,使得相同的构建进程能够创立不同的表明。

  • 原型形式: 经过克隆来创立目标,避免了经过new关键字显式调用结构函数的开支。

结构型形式

  Java中的结构型规划形式首要用于描绘目标之间的联系,包括类和目标的组合、接口和承继等方面。这些形式能够帮助咱们更好地安排和办理代码,提高代码的重用性和可保护性。下面是Java中的7种结构型规划形式:

  • 适配器形式:将一个类的接口转换成客户期望的另一个接口,使得本来由于接口不兼容而无法一起作业的类能够一起作业。

  • 桥接形式:将笼统部分与它的完成部别离离,以便它们能够独立地改变。

  • 组合形式:将目标组合成树形结构以表明“部分-全体”的层次结构,使得客户端运用单个目标或许组合目标具有一致性。

  • 装饰器形式:动态地给一个目标增加一些额定的责任,就增加功用而言,装饰器形式比生成子类办法更为灵活。

  • 外观形式:为子体系中的一组接口供给一个一致的界面,使得子体系更简略运用。

  • 享元形式:运用同享技术来有效地支撑很多细粒度目标的复用。

  • 代理形式:为其他目标供给一种代理以控制对这个目标的访问。

行为型形式

  Java中的行为型规划形式首要用于描绘目标之间的通信和协作办法,包括算法、责任链、状况等方面。这些形式能够帮助咱们更好地安排和办理代码,提高代码的可保护性和可扩展性。下面是Java中的11种行为型规划形式:

  • 责任链形式:为解除恳求的发送者和接收者之间的耦合,而将恳求的处理目标连成一条链,并沿着这条链传递恳求,直到有一个目标处理它为止。

  • 指令形式:将一个恳求封装为一个目标,然后使你可用不同的恳求对客户进行参数化;对恳求排队或记录恳求日志,以及支撑可撤销的操作。

  • 解说器形式:给定一个语言,界说它的文法的一种表明,并界说一个解说器,运用该解说器来解说语言中的句子。

  • 迭代器形式:供给一种办法顺序访问一个聚合目标中各个元素,而又不需求暴露该目标的内部表明。

  • 中介者形式:用一个中介目标封装一系列的目标交互,使得这些目标不需求显现地彼此引证,然后下降耦合度。

  • 备忘录形式:在不破坏封装性的前提下,捕获一个目标的内部状况,并在该目标之外保存这个状况。

  • 调查者形式:界说目标间的一种一对多的依靠联系,当一个目标的状况产生改动时,所有依靠于它的目标都得到告诉并自动更新。

  • 状况形式:答应一个目标在其内部状况产生改动时改动其行为,目标看起来好像修改了它的类。

  • 战略形式:界说一系列的算法,将每个算法封装起来,并使它们之间能够互换。

  • 模板办法形式:界说一个操作中的算法骨架,将一些步骤延迟到子类中。模板办法使得子类能够在不改动算法结构的情况下从头界说算法中的某些步骤。

  • 过滤器规划形式:答应在不改动原始目标的情况下,动态地增加或删去目标的行为。

规划准则与规划形式

  在了解完规划形式之后,咱们再来了解下:六大规划准则

一文总结Java的23种设计模式

  1. 单一责任准则:一个类应该只要一个引起它改变的原因。换句话说,一个类应该只要一项责任。这样能够确保类的内聚性,并且下降类之间的耦合性。

  2. 开闭准则:一个软件实体如类、模块和函数应该对扩展敞开,对修改关闭。这意味着当需求增加新功用时,应该尽量经过扩展已有代码来完成,而不是修改已有代码。

  3. 里氏替换准则:子类应该能够替换父类并且不影响程序的正确性。这意味着在运用承继时,子类不能修改父类已有的行为,而只能扩展父类的功用。

  4. 接口阻隔准则:客户端不应该依靠于它不需求的接口。一个类应该只供给它需求的接口,而不应该强迫客户端依靠于它不需求的接口。

  5. 依靠倒置准则:高层模块不应该依靠于低层模块,它们都应该依靠于笼统。笼统不应该依靠于详细完成,而详细完成应该依靠于笼统。

  6. 迪米特法则:一个目标应该对其他目标保持最少的了解。换句话说,一个目标只应该与它直接彼此作用的目标产生交互,而不应该与其它任何目标产生直接的交互。这样能够下降类之间的耦合性,提高体系的灵活性和可保护性。

规划形式与规划准则他们有什么不同呢?

  规划准则和规划形式是面向目标规划中的两个重要概念,它们彼此关联,但又有不同的含义和作用:

  • 规划准则是一些通用的规划指导方针,它们供给了怎么规划一个优秀的软件体系的基本思想和规矩。指导着规划者怎么安排代码以完成高内聚、低耦合、易扩展和易保护的软件体系。

  • 规划形式则是在特定情况下处理常见问题的经验性处理方案,它们供给了怎么完成这些规划准则的详细办法。

  规划形式往往是在满足规划准则的基础上被运用的。规划形式能够看作是完成规划准则的一种详细办法。

组合运用

  在实践开发中,咱们很少只运用单一的规划形式来处理问题,而是将多种规划形式混合运用,以达到更好的作用。

工厂形式 + 单例形式

  运用工厂形式来创立目标,经过单例形式来确保该工厂只要一个实例,然后削减创立目标时的开支。

  首要,创立一个工厂类,该类运用单例形式来确保只要一个实例,该实例担任创立目标。然后,依据需求创立多个工厂办法,每个办法用于创立不同的目标。

public class SingletonFactory {
    private static SingletonFactory instance;
    private SingletonFactory() {
        // 私有结构办法
    }
    public static SingletonFactory getInstance() {
        if (instance == null) {
            synchronized (SingletonFactory.class) {
                if (instance == null) {
                    instance = new SingletonFactory();
                }
            }
        }
        return instance;
    }
    public Object createObject(String type) {
        if ("type1".equals(type)) {
            return new Type1();
        } else if ("type2".equals(type)) {
            return new Type2();
        } else {
            throw new IllegalArgumentException("Unsupported type: " + type);
        }
    }
}
public class Type1 {
    // 类型1完成逻辑
}
public class Type2 {
    // 类型2完成逻辑
}

  SingletonFactory 类运用两层检查确定完成了单例形式,一起供给了一个 createObject() 办法,该办法依据输入的参数来创立不同的目标。Type1 和 Type2 类别离代表了不同类型的目标,它们包括了各自的完成逻辑。

模板办法形式 + 战略形式

  运用模板办法形式来界说算法的骨架,一起运用战略形式来界说算法的不同完成办法,以完成更高的灵活性。

  假定咱们要完成一个图片处理程序,能够对不同类型的图片进行处理,包括缩放、旋转和裁剪等操作。详细的处理算法能够依据不同类型的图片而异。

  首要,咱们界说一个笼统类 ImageProcessor,它包括一个模板办法 processImage(),该办法界说了一系列的处理步骤,包括打开图片、履行详细的处理算法和保存图片等。其间,详细的处理算法是由战略形式来完成的,咱们运用一个笼统战略接口 ImageProcessingStrategy 来界说不同的处理算法。

public abstract class ImageProcessor {
    public void processImage() {
        BufferedImage image = openImage();
        ImageProcessingStrategy strategy = createImageProcessingStrategy();
        BufferedImage processedImage = strategy.processImage(image);
        saveImage(processedImage);
    }
    protected BufferedImage openImage() {
        // 打开图片的详细完成
    }
    protected abstract ImageProcessingStrategy createImageProcessingStrategy();
    protected void saveImage(BufferedImage image) {
        // 保存图片的详细完成
    }
}
public interface ImageProcessingStrategy {
    BufferedImage processImage(BufferedImage image);
}

  然后,界说详细的图片处理类 JpegProcessorPngProcessor,它们别离承继自 ImageProcessor,并完成 createImageProcessingStrategy() 办法,回来不同的处理算法战略。

public class JpegProcessor extends ImageProcessor {
    @Override
    protected ImageProcessingStrategy createImageProcessingStrategy() {
        return new JpegProcessingStrategy();
    }
}
public class PngProcessor extends ImageProcessor {
    @Override
    protected ImageProcessingStrategy createImageProcessingStrategy() {
        return new PngProcessingStrategy();
    }
}

  最终,界说不同的处理算法战略,例如 JpegProcessingStrategyPngProcessingStrategy,它们完成了 ImageProcessingStrategy 接口,供给了详细的处理算法完成。

public class JpegProcessingStrategy implements ImageProcessingStrategy {
    @Override
    public BufferedImage processImage(BufferedImage image) {
        // Jpeg 图片处理算法
    }
}
public class PngProcessingStrategy implements ImageProcessingStrategy {
    @Override
    public BufferedImage processImage(BufferedImage image) {
        // Png 图片处理算法
    }
}

  这样,完成一个可扩展和可定制的图片处理程序。在运行时,咱们能够依据不同的图片类型挑选不同的处理算法,然后完成不同的图片处理作用。

战略形式 + 工厂形式

  运用工厂形式来创立不同的战略目标,然后运用战略形式来挑选不同的战略,以完成不同的功用。咱们完成一个简略的计算器。

  首要,咱们界说一个 CalculatorStrategy 接口,其间包括了两个办法:calculate 用于计算两个数的结果,getDescription 用于获取当前战略的描绘信息。

public interface CalculatorStrategy {
    double calculate(double num1, double num2);
    String getDescription();
}

  然后,咱们完成几个详细的计算战略类,如加法、减法、乘法和除法:

public class AddStrategy implements CalculatorStrategy {
    @Override
    public double calculate(double num1, double num2) {
        return num1 + num2;
    }
    @Override
    public String getDescription() {
        return "加法";
    }
}
public class SubtractStrategy implements CalculatorStrategy {
    @Override
    public double calculate(double num1, double num2) {
        return num1 - num2;
    }
    @Override
    public String getDescription() {
        return "减法";
    }
}
public class MultiplyStrategy implements CalculatorStrategy {
    @Override
    public double calculate(double num1, double num2) {
        return num1 * num2;
    }
    @Override
    public String getDescription() {
        return "乘法";
    }
}
public class DivideStrategy implements CalculatorStrategy {
    @Override
    public double calculate(double num1, double num2) {
        return num1 / num2;
    }
    @Override
    public String getDescription() {
        return "除法";
    }
}

  接下来,咱们运用工厂形式来创立详细的战略目标。咱们创立一个 CalculatorStrategyFactory 工厂类,其间界说了一个 getCalculatorStrategy 办法,依据传入的操作符,回来相应的计算战略目标。

public class CalculatorStrategyFactory {
    public static CalculatorStrategy getCalculatorStrategy(String operator) {
        switch (operator) {
            case "+":
                return new AddStrategy();
            case "-":
                return new SubtractStrategy();
            case "*":
                return new MultiplyStrategy();
            case "/":
                return new DivideStrategy();
            default:
                throw new IllegalArgumentException("无效的操作符:" + operator);
        }
    }
} 

  咱们创立一个 Calculator 类,其间包括一个 calculate 办法,依据传入的两个数和操作符,回来计算结果。

public class Calculator {
    public static double calculate(double num1, double num2, String operator) {
        CalculatorStrategy calculatorStrategy = CalculatorStrategyFactory.getCalculatorStrategy(operator);
        System.out.println("正在履行 " + calculatorStrategy.getDescription() + " 计算");
        return calculatorStrategy.calculate(num1, num2);
    }
}
//调用计算器
// 履行加法计算
double result = Calculator.calculate(10, 5, "+"); 
 // 输出 15.0
System.out.println(result);

适配器形式 + 装饰器形式

  适配器形式用于将一个接口转换成另一个接口,而装饰器形式则用于动态地给目标增加一些额定的责任。当咱们需求将一个已有的接口转换成新的接口,并且还需求给目标增加一些额定的责任时,能够运用这两个形式混合运用。

  首要界说一个需求被适配的接口:

public interface MediaPlayer {
   public void play(String audioType, String fileName);
}

  然后界说一个能够播映 mp3 文件的详细类:

public class Mp3Player implements MediaPlayer {
   @Override
   public void play(String audioType, String fileName) {
      if(audioType.equalsIgnoreCase("mp3")){
         System.out.println("Playing mp3 file. Name: " + fileName);         
      } 
   }
}

  接下来界说一个适配器,将 AdvancedMediaPlayer 接口转换成 MediaPlayer 接口

public class MediaPlayerAdapter implements MediaPlayer {
   AdvancedMediaPlayer advancedMusicPlayer;
   public MediaPlayerAdapter(AdvancedMediaPlayer advancedMusicPlayer){
      this.advancedMusicPlayer = advancedMusicPlayer;
   }
   @Override
   public void play(String audioType, String fileName) {
      if(audioType.equalsIgnoreCase("vlc") 
         || audioType.equalsIgnoreCase("mp4")){
         advancedMusicPlayer.play(audioType, fileName);
      }
      else if(audioType.equalsIgnoreCase("mp3")){
         System.out.println("Playing mp3 file. Name: " + fileName);
      }
   }
}

  然后界说一个装饰器类,用于在播映 mp3 文件时,动态地增加一些额定的功用:

public class Mp3PlayerDecorator implements MediaPlayer {
   private MediaPlayer mediaPlayer;
   public Mp3PlayerDecorator(MediaPlayer mediaPlayer) {
      this.mediaPlayer = mediaPlayer;
   }
   @Override
   public void play(String audioType, String fileName) {
      if(audioType.equalsIgnoreCase("mp3")){
         System.out.println("Playing mp3 file with additional features. Name: " + fileName);
         // 增加额定的功用
         System.out.println("Adding equalizer to the mp3 file.");
         mediaPlayer.play(audioType, fileName);
      }
      else {
         mediaPlayer.play(audioType, fileName);
      }
   }
}

  最终,咱们能够运用适配器和装饰器来播映不同类型的音频文件:

public static void main(String[] args) {
   MediaPlayer mediaPlayer = new Mp3Player();
   mediaPlayer.play("mp3", "song.mp3");
   AdvancedMediaPlayer advancedMediaPlayer = new VlcPlayer();
   MediaPlayer mediaPlayerAdapter = new MediaPlayerAdapter(advancedMediaPlayer);
   mediaPlayerAdapter.play("vlc", "movie.vlc");
   MediaPlayer decoratedMediaPlayer = new Mp3PlayerDecorator(mediaPlayer);
   decoratedMediaPlayer.play("mp3", "song.mp3");
}

  输出结果

Playing mp3 file. Name: song.mp3
Playing vlc file. Name: movie.vlc
Playing mp3 file with additional features. Name: song.mp3
Adding equalizer to the mp3 file.
Playing mp3 file. Name: song.mp3

调查者形式 + 指令形式

  调查者形式用于调查目标的状况改变,并及时告诉调查者。而指令形式则用于将一个恳求封装成一个目标,能够在运行时动态地切换指令的接收者。当咱们需求调查目标的状况改变,并在状况改变时履行一些指令时,能够运用这两个形式混合运用。

  首要,咱们创立一个接口 Observer 来表明调查者目标,其间包括一个 update() 办法用于更新调查者状况。

public interface Observer {
    void update();
}

  接着,咱们创立一个类 Subject 来表明被调查者目标,其间包括一些调查者目标的引证和一些办法用于注册、刊出和告诉调查者。

import java.util.ArrayList;
import java.util.List;
public class Subject {
    private List<Observer> observers = new ArrayList<>();
    public void register(Observer observer) {
        observers.add(observer);
    }
    public void unregister(Observer observer) {
        observers.remove(observer);
    }
    public void notifyObservers() {
        for (Observer observer : observers) {
            observer.update();
        }
    }
}

  接下来,咱们创立一个接口 Command 来表明指令目标,其间包括一个 execute() 办法用于履行指令。

public interface Command {
    void execute();
}

  然后,咱们创立一个详细指令类 ConcreteCommand,该类完成了 Command 接口,其间包括一个 Subject 目标的引证和一个 execute() 办法,该办法会调用 Subject 目标的 notifyObservers() 办法告诉调查者目标。

public class ConcreteCommand implements Command {
    private Subject subject;
    public ConcreteCommand(Subject subject) {
        this.subject = subject;
    }
    @Override
    public void execute() {
        subject.notifyObservers();
    }
}

  最终,咱们创立一个详细调查者类 ConcreteObserver,该类完成了 Observer 接口,其间包括一个 execute() 办法,该办法会输出一条音讯。

public class ConcreteObserver implements Observer {
    @Override
    public void update() {
        System.out.println("ConcreteObserver received notification.");
    }
}

  现在,咱们能够运用以下代码将调查者形式和指令形式混合运用:

public class Client {
    public static void main(String[] args) {
        // 创立一个被调查者目标
        Subject subject = new Subject();
        // 创立一个调查者目标
        Observer observer = new ConcreteObserver();
        // 注册调查者目标
        subject.register(observer);
        // 创立一个指令目标
        Command command = new ConcreteCommand(subject);
        // 履行指令,告诉调查者目标
        command.execute();
    }
}
//输出  ConcreteObserver received notification.

小结

  需求留意的是,混合运用规划形式要慎重,不要过度运用规划形式,避免代码过于杂乱和难以保护。挑选适宜的规划形式和适宜的组合办法,能够使代码愈加简洁、高效和易于保护。