设计模式

设计模式是在软件工程中反复出现的一些问题的典型解决方案。他们可以帮助开发者以可重用和可维护的方式解决常见的软件设计问题。设计模式是经过验证的最佳实践,通常被用来提高代码的可读性,灵活性和可维护性。

创建模式

工厂方法模式

工厂方法模式是一种创建型设计模式,他的目的是封装对象的创建过程,是的客户端不需要知道具体对象的创建细节。在工厂方法模式中,我们不会指定要创建哪个类的对象,而是由子类决定要创建哪个类的对象。这样我们就可以将对象的创建从代码中解耦,使得代码更加灵活。

这种模式通过定义一个创建对象的接口,让实现这个接口的子类来决定实例化哪个类。

模式结构

工厂方法模式包含以下角色:

  1. 抽象工厂:提供一个创建产品的接口,它包含一个创建产品的抽象方法。

  2. 具体工厂:实现抽象工厂接口中的创建产品方法,返回具体的产品实例。

  3. 抽象产品:定义一个产品的抽象类,它包含了一些基本的属性和方法,这些方法或属性将会被具体的产品类所实现和覆盖。

  4. 具体产品:实现抽象产品接口中定义的方法和属性。

实现方法

创建一个Shape接口和实现Shape接口的实体类。

创建一个接口

public intertace Shape {
    void draw();
}

创建实现接口的实体类

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

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

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

定义工厂类ShapeFactory,项目使用ShapeFactory来获取Shape对象。

创建一个工厂,生成基于给定信息的实体类的对象

public class ShapeFactory {
    
    //使用getShape方法获取形状类型呃对象
    public Shape getShape(String shapeType){
        if(shapeType == null)
        {
            return null;
        }
        if(shapeType.equalsIgnoreCase("CIRCLE"))
        {
            return new Circle();
        }
        else if(shapeType.equalsIgnoreCase("RECTANGLE"))
        {
            return new Rectangle();
        }
        else if(shapeType.equalsIgnoreCase("SQUARE"))
        {
            return new Square();
        }
        return null;
    }
}
调用方法
public class Test {

     public static void main(String[] args)
     {
          ShapeFactory shapeFactory = new ShapeFactory();
          
          //获取Circle的对象,并调用它的draw方法
          Shape shape1 = shapeFactory.getShape("CIRCLE");
          shape1.draw();
          
          //获取SQUARE的对象,并调用它的draw方法
          Shape shape2 = shapeFactory.getShape("SQUARE");
          shape2.draw();
          
          //获取RECTANGLE的对象,并调用它的draw方法
          Shape shape3 = shapeFactory.getShape("RECTANGLE");
          shape3.draw();
     }
}
优点

  1. 工厂方法模式将产品的实现和客户端代码隔离开来,使得客户端代码无需关心具体的产品实现细节。

  2. 工厂方法模式遵循了单一职责原则,每个工厂只负责生成一个产品,符合高内聚低耦合的设计思想。

  3. 工厂方法模式可以很方便地扩展新的产品,只需要添加相应的产品类和工厂类即可。

  4. 工厂方法模式可以很容易地用来创建工厂的实现,这使得代码更加灵活。

抽象工厂模式

抽象工厂模式是一种创建型设计模式,它提供了一种创建一系列相关或相互依赖对象的接口,而不需要指定它们的具体类。这种模式的主要目的是通过使用多态性来封装对象的创建过程,使得客户端可以独立于具体产品类的创建。

模式结构

抽象工厂模式包含以下角色:

  1. 抽象工厂:定义创建一系列抽象产品的接口。

  2. 具体工厂:实现抽象工厂接口,创建具体产品。

  3. 抽象产品:定义产品接口或抽象类。

  4. 具体产品:实现抽象产品接口。

实现方法

定义第一个接口

public interface Fruit {
     void product();
}

实现第一个接口的类

public class Apple implements Fruit {
     @Override
     public void product(){
          System.out.println("水果--苹果");
     }
}

public class Banana implements Fruit {
     @Override
     public void product(){
          System.out.println("水果--香蕉");
     }
}

public class Grape implements Fruit {
     @Override
     public void product(){
          System.out.println("水果--葡萄");
     }
}

定义第二个接口

public interface Vegetable {
     void product();
}

实现第二个接口的类

public class Eggplant implements Vegetable {
     @Override
     public void product(){
          System.out.println("蔬菜--茄子");
     }
}

public class Cabbage implements Vegetable {
     @Override
     public void product(){
          System.out.println("蔬菜--卷心菜");
     }
}

public class Carrot implements Vegetable {
     @Override
     public void product(){
          System.out.println("蔬菜--胡萝卜");
     }
}

创建抽象工厂类

public abstract class AbstractFactory {
     public abstract Fruit getFruit(String fruit);
     public abstract Vegetable getVegetable(String vegetable);
}

创建扩展了抽象工厂(AbstractFactory)的工厂类

public class FruitFactory extends AbstractFactory {
     @Override
     public Fruit newFruit(String FruitType)
     {
          if(FruitType.equals("苹果"))
          {
               return new Apple();
          }
          else if(FruitType.equals("香蕉"))
          {
               return new Banana();
          }
          else if(FruitType.equals("葡萄"))
          {
               return new Grape();
          }
          else
          {
               return null;
          }
     }
}

public class VegetableFactory extends AbstractFactory {
    @Override
    public Vegetable newVegetable(String VegetableType)
    {
         if(VegetableType.equals("茄子"))
         {
             return new Eggplant();
         }
         
         else if(VegetableType.equals("茄子"))
         {
             return new Eggplant();
         }
         
         else if(VegetableType.equals("茄子"))
         {
             return new Eggplant();
         }
         else
         {
             return null;
         }
    }
}

创建一个工厂生成器类

public class FactoryProducer {
     public staic AbstractFactory(String choice) {
         if(choice.equalsIgnoreCase('水果'))
         {
              return new FruitFactory();
         }
         else if(choice.equalsIgnoreCase('蔬菜'))
         {
              return new VegetableFactory();
         }
         else
         {
              return null;
         }
     }
}
调用方法
public class Test {
    public static void main(String[] args) {
 
        AbstractFactory fruitFactory = FactoryProducer.getFactory("水果");
        AbstractFactory vegetableFactory = FactoryProducer.getFactory("蔬菜");
 
        Fruit fruit = fruitFactory.newFruit("苹果");
        fruit.product();
        Fruit fruit2 = fruitFactory.newFruit("香蕉");
        fruit2.product();
        Fruit fruit3 = fruitFactory.newFruit("葡萄");
        fruit3.product();
 
        Vegetable vegetable = vegetableFactory.newVegetable("茄子");
        vegetable.product();
        Vegetable vegetable2 = vegetableFactory.newVegetable("卷心菜");
        vegetable2.product();
        Vegetable vegetable3 = vegetableFactory.newVegetable("胡萝卜");
        vegetable3.product();
        
 
    }
}

工厂方法模式与抽象工厂模式的异同

共同点:

  1. 两种模式都封装了对象的创建过程,使得客户端代码不需要知道具体的产品类是如何实现的。

  2. 两种模式都支持开闭原则,即在不修改现有代码的情况下,可以引入新的对象类型。

  3. 两种模式都将对象的创建和使用分离,降低了系统各部分之间的耦合度。

  4. 两种模式都利用多态性,通过抽象类或者接口来引用具体产品。

不同点

  1. 在目的方面,工厂方法模式主要用于创建一个具体产品的实例,它针对的是一个产品等级结构;而抽象工厂模式用于创建一系列相关的产品对象,它针对的是多个产品等级结构。

  2. 在结构方面,工厂方法模式包含一个抽象产品类,多个具体产品类,一个抽象工厂类,多个具体工厂类;而抽象工厂模式包含多个抽象产品类,多个具体产品类,一个抽象工厂类,多个具体工厂类。

  3. 工厂方法模式中增加新的产品时,需要在抽象工厂中添加新的工厂方法;抽象工厂模式中增加新的产品族时,需要添加新的抽象工厂和一系列具体工厂。

  4. 工厂方法模式通常只创建一个产品实例;而抽象工厂模式可以创建多个产品实例,这些产品构成一个产品族。

概括来讲就是工厂方法模式更适合于单一产品的创建,他强调的是一个工厂创建一个产品;抽象工厂模式更适合于多个相关产品的创建,它强调的是一个工厂创建一系列相关的产品。

单例模式

单例模式(Singleton Pattern)是指确保一个类在任何情况下都绝对只有一个实例,并提供一个全局访问点。

即单例模式是在内存中仅会创建一次对象的设计模式

特点
  1. 单例类只有一个实例对象。

  2. 该单例对象必须由单例类自行创建。

  3. 单例类对外提供一个访问该单例的全局访问点。

常见单例模式及其写法
饿汉式单例

饿汉式单例是在类加载的时候就立即初始化,并且创建单例对象,在程序调用时直接返回该单例对象即可。

写法:

public class Test {
     //私有化构造器
     private Test(){
     
     }
     //在类的内部创建自行实例
     private static final Test instance = new Test();
     //提供获取唯一实例的方法(全局访问点)
     public static Test getInstance(){
          return instance;
     }
}

缺点:类加载的时候就初始化,不管用与不用都占着空间,浪费内存。

懒汉式单例

懒汉式单例指类加载的时候不会导致该单实例对象被创建,首次使用该对象时该单实例对象才会被创建。

即被外部类调用的时候内部类才会加载。

写法

写法基于饿汉式单例的写法,在提供获取实例方法的位置增加判断语句(判断该对象是否已经实例化)

public class Test {

    private Test() {
    
    }
    
    private static Test instance = null;
    
    public static Test getInstance() {
        if(instance == null) {
            instance = new Test();
        }
        return instance;
    }
}

上面的写法存在线程安全隐患,我们可以对共享资源进行加锁来,保证在同一时刻只能有一个线程拿到该资源。

public class Test {

    private Test() {
    
    }
    
    private static Test instance = null;
    
    public synchronized static Test getInstance() {
        if(instance == null) {
            instance = new Test();
        }
        return instance;
    }
}

此为简单保证线程安全的方法,该方法虽然能解决线程安全问题,但是在线程较多情况下会出现阻塞,导致程序运行性能大幅下降。

为了兼顾线程安全又能提升程序性能,我们可以使用双重检查锁懒汉式来解决

public class Test {

    private Test() {
    
    }
    
    private static volatile Test instance = null;
    
    public static Test getInstance() {
        if(instance == null)
        {
            synchronized(Test.class){
                if(instance == null){
                    instance = new Test();
                }
            }
        }
        return instance; 
    }
}

或者静态内部类懒汉式(相较于双重检查锁懒汉式更为简便)

public class Test {
    
    private Test() {
        
    }
    
    public static final Test getInstance() {
        return LazyHolder.INSTANCE;
    }
    
    private static class Test {
        private static final Test INSTANCE = new Test();
    }
}

原理:

利用的额原理就是类的加载初始化顺序:

  1. 当类不被调用的时候,类的静态内部类是不会进行初始化的,这就避免了内存浪费问题

  2. 当有方法调用getInstance()方法时,会先初始化静态内部类,而静态内部类中的成员变量是final的,所以即使是多线程,其成员变量是不会被修改的,所以就解决了添加synchronized所带来的性能问题。

注册式(登记式)单例

注册式单例又称为登记式单例,就是将每一个实例都登记到某一个地方,使用唯一的标识获取实例。

注册式单例有两种写法:一种为容器缓存,一种为枚举登记。

枚举式单例

枚举类型是Java中实现单例模式的一种非常有效的方式,因为它天然就是线程安全的,并且可以防止反序列化重新创建新的对象。枚举式单例可避免序列化破坏和反射破坏。

public class EnumResource {

}

public enum EnumSingleton {
    INSTANCE;      //单例实例
    
    private Object instance;
    
    EnumSingleton() {
        instance = new EnumResource();
    }
    
    public Object getInstance() {
       return instance;
 
    }
}
容器式单例

容器式单例适用于创建实例非常多的情况,便于管理,但是线程不安全

public class ContainerSingleton {

    private ContainerSingleton() {
    }

    private static Map<String, Object> ioc = new ConcurrentHashMap<String, Object>();

    public static Object getBean(String className) {
        synchronized (ioc) {
            if (!ioc.containsKey(className)) {
                Object object = null;
                try {
                    object = Class.forName(className).newInstance();
                    ioc.put(className, object);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                return object;
            } else 
            {
                return ioc.get(className);
            }
        }
    }
}

建造者模式

建造者模式是一种创建型设计模式,允许分布创建复杂对象。这种模式通过将对象的构建过程与其表示分离,使得你可以创建不同表示形式的对象。

适用于当创建的对象很复杂,或者当对象的创建过程需要多个步骤,且这些步骤经常变化时。

作用:在用户不知道对象的建造过程和细节的情况下就可以直接创建复杂的对象。用户只需要给出指定复杂对象的类型和内容即可,而建造者模式负责按顺序创建复杂对象(把内部的建造过程和细节隐藏起来)

模式结构
  1. 产品(Product):要构建的复杂对象。

  2. 抽象建造者(Builder):定义了构建产品的各个部件的接口。

  3. 具体建造者(Concrete Builder):实现Builder接口,提供构建逻辑的具体实现

  4. 指挥者(Director):负责安排复杂对象的建造次序,他与抽象建造者之间存在关联关系,并在其中调用建造者对象的部件构造与装配方法。

  5. 客户端(Client):客户端代码使用具体建造者对象创建产品对象。

实例展示

买电脑实例

定义组装电脑的过程(Builder)

public  abstract class Builder {  

//第一步:装CPU
    public abstract void  BuildCPU();

//第二步:装主板
    public abstract void BuildMainboard();

//第三步:装硬盘
    public abstract void BuildHD();

//返回产品的方法:获得组装好的电脑
    public abstract Computer GetComputer();
}

委派任务给装机人员(Director)

public class Director{

    //指挥装机人员组装电脑
    public void Construct(Builder builder)
    {

         builder. BuildCPU();
         builder.BuildMainboard();
         builder. BuildHD();
                              
    }
 }

创建具体的建造者(ConcreteBuilder)

public class ConcreteBuilder extend  Builder{
    //创建产品实例
    Computer computer = new Computer();

    //组装产品
    @Override
    public void  BuildCPU(){  
       computer.Add("CPU")
    }  

    @Override
    public void  BuildMainboard(){  
       computer.Add("主板")
    }  

    @Override
    public void  BuildHD(){  
       computer.Add("显示器")
    }  

    //返回组装成功的电脑
     @Override
      public  Computer GetComputer(){  
      return computer
    }  
}

定义具体产品类(Product)

public class Computer{

    //电脑组件的集合
    private List<String> parts = new ArrayList<String>();

    //用于将组件组装到电脑里
    public void Add(String part){
    parts.add(part);
}

    public void Show(){
          for (int i = 0;i<parts.size();i++){    
          System.out.println(“组件”+parts.get(i)+“装好了”);
          }
          System.out.println(“电脑组装完成,请验收”);
}
}

客户端使用具体建造者对象创建产品对象

public class Builder Pattern{

  public static void main(String[] args){

//定义指挥者和抽象建造者
  Director director = new Director();
  Builder builder = new ConcreteBuilder();

//指挥者指使装机人员去装电脑
director.Construct(builder);

//电脑组装完毕
Computer computer = builder.GetComputer();
computer.Show();

    }

}

 组件CPU装好了
组件主板装好了 
组件硬盘装好了
电脑组装完成,请验收

 

优缺点

优点

  • 灵活性:可以在不增加更多构造函数的情况下创建配置各异的对象。

  • 可读性:提供了流畅的接口,使代码更易读。

  • 不可变性:可以在构建完成后创建不可变对象,保证线程安全并防止意外修改。

建造者模式与工厂模式相比,建造者模式通常用于创建更复杂的对象,而工厂模式通常用于创建简单的对象

;建造者模式允许用户分步骤创建对象,而工厂模式通常在一步中创建对象;建造者模式关注于构建复杂对象,工厂模式关注于封装对象的创建过程。

缺点

建造者模式所创建的产品一般具有较多的共同点,其组成部分相似,如果产品之间的差异性很大,则不适合使用建造者模式,因此其使用范围受到一定的限制。

使用场景
  • 创建的对象较复杂,由多个部件构成,各部件面临着复杂的变化,但构件间的建造顺序是稳定的。

  • 创建复杂对象的算法独立于该对象的组成部分以及它们的装配方式,即产品的构建过程和最终的表示是独立的。

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐