设计模式:工厂方法模式(Factory Method)和抽象工厂模式(Abstact Factory)

本文详细介绍了工厂模式的概念及其三种类型:简单工厂模式、工厂方法模式和抽象工厂模式。通过示例代码,展示了每种模式的组成及应用场景,帮助读者理解如何在实际开发中应用这些模式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 在面向对象编程中, 最通常的方法是一个new操作符产生一个对象实例,new操作符就是用来构造对象实例的。但是在一些情况下, new操作符直接生成对象会带来一些问题。举例来说, 许多类型对象的创造需要一系列的步骤: 你可能需要计算或取得对象的初始设置; 选择生成哪个子对象实例; 或在生成你需要的对象之前必须先生成一些辅助功能的对象。 在这些情况,新对象的建立就是一个 “过程”,不仅是一个操作,像一部大机器中的一个齿轮传动。


分类

 工厂模式主要是为创建对象提供过渡接口,以便将创建对象的具体过程屏蔽隔离起来,达到提高灵活性的目的。
 工厂模式可以分为三类:

  1. 简单工厂模式(Simple Factory)
  2. 工厂方法模式(Factory Method)
  3. 抽象工厂模式(Abstract Factory)

 这三种模式从上到下逐步抽象,并且更具一般性。
 GOF在《设计模式》一书中将工厂模式分为两类:工厂方法模式(Factory Method)与抽象工厂模式(Abstract Factory)。将简单工厂模式(Simple Factory)看为工厂方法模式的一种特例,两者归为一类。


简单工厂模式

 简单工模式时类的创建模式,又叫做静态工厂方法(static Factory Method)。简单工厂模式是一个工厂对象决定创建出哪一种产品类的实例。它存在的目的很简单:定义一个创建对象的接口。
 组成:

  1. 工厂类角色:这是本模式的核心,含有一定的商业逻辑和判断逻辑。在java中它往往由一个具体类实现。
  2. 抽象产品角色:它一般是具体产品继承的父类或者实现的接口。在java中由接口或者抽象类来实现。
  3. 具体产品角色:工厂类所创建的对象就是此角色的实例。在java中由一个具体类实现。

 举个简单例子:
1 抽象产品角色

public interface IProduct
{
    public void method();
}

2 具体产品角色(可以把方法定义为static的)

public class ProductA implements IProduct
{
    @Override
    public void method()
    {
        System.out.println("I'm ProductA!");
    }
}

public class ProductB implements IProduct
{
    @Override
    public void method()
    {
        System.out.println("I'm ProductB!");
    }
}

3 工厂类角色

public class SimpleFactory
{
    public IProduct produce(String type)
    {
        if("A".equals(type))
        {
            return new ProductA();
        }
        else if("B".equals(type))
        {
            return new ProductB();
        }
        else
        {
            System.out.println("请输入正确的类型");
            return null;
        }
    }
}

4 测试类

public class MainTest
{
    public static void main(String args[])
    {
        SimpleFactory factory = new SimpleFactory();
        IProduct product = factory.produce("A");
        product.method();
    }
}

输出:I’m ProductA!

 在Java中java.text.DateFormat就是简单工厂模式的典型案例。
优点:专门定义一个工厂类负责创建其他类的实例,最大的优点在于工厂类中包含了必要的逻辑,根据客户需要的条件动态实例化相关的类。
缺点:当需要增加一种产品时,比如ProductC就需要修改简单工厂类SimpleFactory(增加if-else块),这违背了开闭原则。

TIPS
 其实如果采用反射机制实现简单工厂并没有违背开闭原则。
 利用反射机制,将简单工厂类改成:

public class SimpleFactory
{
    public IProduct produce(Class<? extends IProduct> c) throws Exception
    {
        return (IProduct)Class.forName(c.getName()).newInstance();
//        return (IProduct)c.newInstance();        //或者采用这种方法
    }
}

 测试类:

public class MainTest
{
    public static void main(String args[]) throws Exception
    {
        SimpleFactory factory = new SimpleFactory();
        IProduct product = factory.produce(ProductA.class);
        product.method();
    }
}

 这样当有新的产品时,其实并不需要修改工厂类。《Effective Java(Second Edition)》中明确指出:通常,普通应用程序在运行时不应该以反射方式访问对象。所以本篇文章建立在不采用反射机制的情况下,在下面介绍的工厂方法模式其实也可以改用反射机制实现,博主就不展示了。至于《Effective Java(Second Edition)》为什么不推荐反射机制,可以参考此书的“接口优先于反射机制”这一主题,这里不赘述。


工厂方法模式

 工厂方法模式是简单工厂模式的进一步抽象化和推广,工厂方法模式里不再只由一个工厂类决定那一个产品类应当被实例化,这个决定被交给抽象工厂的子类去做。
这里写图片描述
 来看下它的组成:
1. 抽象工厂角色: 这是工厂方法模式的核心,它与应用程序无关。是具体工厂角色必须实现的接口或者必须继承的父类。在java中它由抽象类或者接口来实现。
2. 具体工厂角色:它含有和具体业务逻辑有关的代码。由应用程序调用以创建对应的具体产品的对象。
3. 抽象产品角色:它是具体产品继承的父类或者是实现的接口。在java中一般有抽象类或者接口来实现。
4. 具体产品角色:具体工厂角色所创建的对象就是此角色的实例。在java中由具体的类来实现。
 举个简单例子:
1 抽象工厂角色

public interface IFactory
{
    public IProduct produce();
}

2 具体工厂角色

public class ConcreteFactoryA implements IFactory
{
    @Override
    public IProduct produce()
    {
        return new ProductA();
    }
}

public class ConcreteFactoryB implements IFactory
{
    @Override
    public IProduct produce()
    {
        return new ProductB();
    }
}

3 抽象产品角色(和简单工厂的一样)

public interface IProduct
{
    public void method();
}

4 具体产品角色(和简单工厂的一样)

public class ProductA implements IProduct
{
    @Override
    public void method()
    {
        System.out.println("I'm ProductA!");
    }
}

public class ProductB implements IProduct
{
    @Override
    public void method()
    {
        System.out.println("I'm ProductB!");
    }
}

5 测试代码:

public class MainTest
{
    public static void main(String[] args)
    {
        IFactory factoryA = new ConcreteFactoryA();
        IProduct product1 = factoryA.produce();
        product1.method();
    }
}

输出:I’m ProductA!

工厂方法模式和简单工厂模式在定义上的不同是很明显的。工厂方法模式的核心是一个抽象工厂类,而不像简单工厂模式, 把核心放在一个实类上。工厂方法模式可以允许很多实的工厂类从抽象工厂类继承下来, 从而可以在实际上成为多个简单工厂模式的综合,从而推广了简单工厂模式。
工厂方法相比于简单工厂模式的优点是增加一个产品,只需要增加一个具体工厂类和具体产品类,没有修改原先的工厂类,符合开闭原则。缺点是客户端的代码会需要修改(简单工厂模式的客户端不需要修改),随着产品的继续增加,所要实现的类的个数也会随之增大。


抽象工厂模式

 在抽象工厂模式中,抽象产品 (AbstractProduct) 可能是一个或多个,从而构成一个或多个产品族(Product Family)。 在只有一个产品族的情况下,抽象工厂模式实际上退化到工厂方法模式。
这里写图片描述

工厂方法模式 VS 抽象工厂模式
工厂方法模式:一个抽象产品类,可以派生出多个具体产品类。每个具体工厂类只能创建一个具体产品类的实例。
抽象工厂模式:多个抽象产品类,每个抽象产品类可以派生出多个具体产品类。一个抽象工厂类可以派生出多个具体工厂类。每个具体工厂类可以创建多个具体产品的实例。
区别:工厂方法模式只有一个抽象产品类,而抽象工厂模式有多个。工厂方法模式的具体工厂类只能创建一个具体产品类的实例,而抽象工厂模式可以创建多个。

举个简单例子:
1 抽象产品角色

public interface AbstractProductA
{
    public void produceA();
}
public interface AbstractProductB
{
    public void produceB();
}

2 抽象工厂角色

public interface AbstractFactory
{
    public AbstractProductA CreateProductA();
    public AbstractProductB CreateProductB();
}

3 具体产品角色

public class ProductA1 implements AbstractProductA
{
    @Override
    public void produceA()
    {
        System.out.println("Im ProductA1!");
    }
}
public class ProductA2 implements AbstractProductA
{
    @Override
    public void produceA()
    {
        System.out.println("Im ProductA2!");
    }
}
public class ProductB1 implements AbstractProductB
{
    @Override
    public void produceB()
    {
        System.out.println("Im ProductB1!");
    }
}
public class ProductB2 implements AbstractProductB
{
    @Override
    public void produceB()
    {
        System.out.println("Im ProductB2!");
    }
}

4 具体工厂角色

public class ConcreteFactory1 implements AbstractFactory
{
    @Override
    public AbstractProductA CreateProductA()
    {
        return new ProductA1();
    }

    @Override
    public AbstractProductB CreateProductB()
    {
        return new ProductB1();
    }
}
public class ConcreteFactory2 implements AbstractFactory
{
    @Override
    public AbstractProductA CreateProductA()
    {
        return new ProductA2();
    }

    @Override
    public AbstractProductB CreateProductB()
    {
        return new ProductB2();
    }
}

5 测试代码

public class MainTest
{
    public static void main(String[] args)
    {
        AbstractFactory factory = new ConcreteFactory1();
        AbstractProductA product1 = factory.CreateProductA();
        AbstractProductB product2 = factory.CreateProductB();
        product1.produceA();
        product2.produceB();
    }
}

输出结果:

Im ProductA1!
Im ProductB1!

 抽象工厂的优点:抽象工厂模式除了具有工厂方法模式的优点外,最主要的优点就是可以在类的内部对产品族进行约束。所谓的产品族,一般或多或少的都存在一定的关联,抽象工厂模式就可以在类内部对产品族的关联关系进行定义和描述,而不必专门引入一个新的类来进行管理。
 抽象工厂的缺点:产品族的扩展将是一件十分费力的事情,假如产品族中需要增加一个新的产品,则几乎所有的工厂类都需要进行修改。所以使用抽象工厂模式时,对产品等级结构的划分是非常重要的。

Jdk中的工厂方法
java.lang.Object#toString();
java.lang.Class#newInstance();
java.lang.Class#forName();
java.lang.Boolean#valueOf();
java.lang.Proxy#newProxyInstance();
java.lang.reflect.Array#newInstance();
java.lang.reflect.Constructor#newInstance();
java.util.concurrent.Executors#newCachedThreadPool()等


总结

 无论是简单工厂模式,工厂方法模式,还是抽象工厂模式,他们都属于工厂模式,在形式和特点上也是极为相似的,他们的最终目的都是为了解耦。在使用时,我们不必去在意这个模式到底工厂方法模式还是抽象工厂模式,因为他们之间的演变常常是令人琢磨不透的。经常你会发现,明明使用的工厂方法模式,当新需求来临,稍加修改,加入了一个新方法后,由于类中的产品构成了不同等级结构中的产品族,它就变成抽象工厂模式了;而对于抽象工厂模式,当减少一个方法使的提供的产品不再构成产品族之后,它就演变成了工厂方法模式。所以,在使用工厂模式时,只需要关心降低耦合度的目的是否达到了。


参考资料:
1. 《设计模式(一)工厂模式Factory(创建型)
2. 《学习:java设计模式—工厂模式
3. 《23种设计模式(3):抽象工厂模式
4. 《Effective Java(Second Edition)》Joshua Bloch.
5. 《细数JDK里的设计模式

<think>首先,用户的问题是:“我想了解接口中成员变量是否可以用abstract修饰,并获取相关规则或解释。请问java接口中成员变量abstract修饰规则。”这是一个关于Java接口中成员变量的修饰符规则的问题,特别是abstract修饰符。从系统指令中,我需要:-所有行内数学表达式用$...$格式。-独立公式用$$...$$格式并单独成段。-LaTeX语法正确。-使用中文回答。-生成相关问题。-回答中引用的段落末尾自然地添加引用标识,例如[^1]。用户提供了几个引用:-引用[1]:讨论了abstract、static、final修饰符的区别。-引用[2]:讨论了抽象类的规则,包括抽象方法的修饰符。-引用[3]:明确指出接口中的成员变量默认都是public,static,final类型,不能被abstract修饰。-引用[4]:讨论了变量命名规则修饰符,提到常量通常用final修饰。-引用[5]:提到static变量方法的访问修饰符。关键点是引用[3]:它说“接口中的成员变量默认都是public,static,final类型,必须被显式初始化;”并且“接口中只能包含public,static,final类型成员变量public、abstract类型的成员方法;”。这直接回答了用户的问题:接口中的成员变量不能用abstract修饰,因为它们是隐式的final。在Java中:-接口的成员变量(字段)默认是publicstaticfinal,所以它们是常量,不能被修改,因此不能用abstract修饰,因为abstract用于抽象方法或类,表示不完整需要子类实现。-成员变量是具体的,不能被抽象;只有方法可以是抽象的。所以,规则是:接口中的成员变量不能用abstract修饰。回答结构:1.**清晰解释规则**:直接回答用户的问题,解释为什么不能用abstract修饰。2.**引用支持**:自然地引用引用[3]来支持解释。3.**使用中文**:整个回答用中文。4.**生成相关问题**:在最后添加相关问题部分。5.**格式**:如果有数学内容,用LaTeX,但这个问题是关于Java的,可能没有数学表达式。如果有变量名,可以用$...$,但不是必须的。潜在数学:如果讨论类型或表达式,可以用LaTeX,例如,$int$或类似的东西,但保持简单。最终回答结构:-开头:直接回答问题。-主体:解释规则,引用来源。-结尾:生成相关问题。</think>### Java 接口中成员变量的 abstract 修饰规则 在 Java 中,接口(interface)的成员变量(也称为字段或属性)**不能用 abstract 修饰符修饰**。这是因为接口的成员变量具有固定的默认修饰规则,abstract 修饰符仅适用于方法或类,不适用于变量。以下是详细解释相关规则: #### 规则解释 1. **接口成员变量的默认修饰符**: - 根据 Java 语言规范,接口中的所有成员变量**默认隐式地具有 public、static final 修饰符**。这意味着: - 它们必须是公共的(public),允许任何类访问。 - 它们是静态的(static),属于接口本身,而非实例。 - 它们是最终的(final),必须在声明时显式初始化,且值不可更改。 - 因此,成员变量本质上是常量,不能被抽象化。abstract 修饰符用于定义不完整的方法(需要子类实现)或抽象类,但成员变量是具体的、已初始化的值,无法使用 abstract[^3]。 2. **为什么不能用 abstract 修饰**: - abstract 修饰符的含义是“抽象的”,它要求被修饰的实体(如方法)由子类提供具体实现。但成员变量是数据存储单元,不能有“抽象”状态;它们必须有一个具体的初始值。 - 如果尝试在接口中使用 abstract 修饰成员变量,编译器会报错。例如: ```java public interface MyInterface { abstract int myVar = 10; // 编译错误:Illegal combination of modifiers: 'abstract' and 'final' } ``` 错误原因:abstract 与 final 冲突(final 表示不可变,abstract 表示未实现),这违反了 Java 的语法规则[^2][^3]。 3. **接口中允许的成员类型**: - 接口只能包含: - public static final 成员变量(常量)。 - public abstract 成员方法(默认可省略 abstract 关键字)。 - 从 Java 8 起,还支持 default static 方法。 - 成员变量必须是完全初始化的常量,不允许 abstract、private 或其他修饰符(private 会限制访问,违反接口的公共性)[^3][^5]。 #### 相关规则总结 - **成员变量初始化**:所有接口成员变量必须在声明时显式初始化,例如 `int MAX_SIZE = 100;`[^3][^4]。 - **修饰符冲突**:abstract 不能与 final 或 static 同时用于变量,因为 final 表示不可继承,而 abstract 要求继承实现[^1][^2]。 - **最佳实践**:接口成员变量通常用于定义全局常量,命名应遵循全大写蛇形命名法(如 `MAX_VALUE`),以提高可读性[^4]。 总之,在 Java 接口中,成员变量永远不能用 abstract 修饰,这是语言设计的一致性要求,确保接口作为“契约”的纯粹性[^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值