简单工厂模式(Simple Factory Pattern)是一种创建型设计模式,它通过一个工厂类来封装对象的创建过程,而不需要将具体的创建逻辑暴露给客户端。
简单工厂模式由三个主要角色组成:
Product(产品接口):定义产品的通用行为,客户端通过该接口与产品进行交互。
ConcreteProduct(具体产品类):实现产品接口,定义具体产品的特定行为。
Factory(工厂类):负责创建产品对象的类,它通常是一个静态方法,在方法中根据传入的参数或逻辑决定创建哪个具体产品的实例,并返回给客户端。
话不多说,直接上代码:
public interface Product {
void use();
}
public class ConcreteProductA implements Product {
@Override
public void use() {
System.out.println("Using ConcreteProductA");
}
}
public class ConcreteProductB implements Product {
@Override
public void use() {
System.out.println("Using ConcreteProductB");
}
}
public class ProductFactory {
public static Product createProduct(String type) {
if (type.equals("A")) {
return new ConcreteProductA();
} else if (type.equals("B")) {
return new ConcreteProductB();
}
throw new IllegalArgumentException("Invalid product type: " + type);
}
}
在这个示例中,Product
是产品接口,定义了产品的通用行为。ConcreteProductA
和ConcreteProductB
是具体产品类,实现Product
接口,并定义了各自的特定行为。
ProductFactory
是工厂类,通过静态方法createProduct()
创建具体产品的实例。根据传入的参数来判断应该创建哪个具体产品的实例。
当使用简单工厂模式时,可以通过以下步骤来实现:
定义产品接口(Product):产品接口定义了产品的通用行为和方法。所有具体产品都必须实现该接口。
创建具体产品类(ConcreteProduct):具体产品类实现产品接口,并实现具体的产品行为。
创建工厂类(Factory):工厂类负责根据客户端的请求创建具体的产品对象。通常,工厂类的方法是静态的,以便直接通过类名调用。
下面是一个更详细的示例,演示了如何使用简单工厂模式来创建不同类型的动物:
// 1. 定义产品接口
public interface Animal {
void sound();
}
// 2. 创建具体产品类
public class Dog implements Animal {
@Override
public void sound() {
System.out.println("Woof!");
}
}
public class Cat implements Animal {
@Override
public void sound() {
System.out.println("Meow!");
}
}
// 3. 创建工厂类
public class AnimalFactory {
public static Animal createAnimal(String type) {
if (type.equals("Dog")) {
return new Dog();
} else if (type.equals("Cat")) {
return new Cat();
}
throw new IllegalArgumentException("Invalid animal type: " + type);
}
}
// 客户端代码
public class Client {
public static void main(String[] args) {
Animal dog = AnimalFactory.createAnimal("Dog");
dog.sound(); // Output: Woof!
Animal cat = AnimalFactory.createAnimal("Cat");
cat.sound(); // Output: Meow!
}
}
在这个示例中,我们定义了一个公共的Animal
接口,并创建了Dog
和Cat
两个具体的产品类来实现Animal
接口。
工厂类AnimalFactory
拥有一个静态方法createAnimal()
,根据传入的参数类型来决定创建哪种具体的动物实例。在客户端代码中,我们使用工厂类的静态方法来创建不同类型的动物对象,并执行它们的行为。
当使用简单工厂模式时,还需要考虑如何处理错误情况。在前面的示例中,如果客户端请求创建的产品类型不被支持,工厂方法会抛出IllegalArgumentException
。这可能导致客户端需要处理异常情况,这并非总是理想的做法。
一种更好的方法是在工厂类中提供一种默认行为,当客户端请求创建的产品类型不被支持时,返回一个默认的产品对象或者 null。这样客户端代码就不需要显式地处理异常情况。
下面是对工厂类进行修改,添加默认行为:
// 创建工厂类
public class AnimalFactory {
public static Animal createAnimal(String type) {
if (type.equals("Dog")) {
return new Dog();
} else if (type.equals("Cat")) {
return new Cat();
} else {
System.out.println("Unsupported animal type. Returning default animal.");
return new DefaultAnimal();
}
}
}
在上面的示例中,当客户端请求创建的产品类型不被支持时,工厂方法返回了一个DefaultAnimal
对象,这样客户端代码就无需显式处理异常情况。
简单工厂模式为客户端隐藏了实例化产品对象的复杂过程,提供了一个集中的创建点,有助于简化客户端代码和进一步实现封装。但需要注意的是,简单工厂模式最适合具有固定数量产品类型的场景,在产品类型经常变化或需要增加时,维护工厂类的逻辑可能会变得复杂。
开闭原则是面向对象设计中的一个基本原则,也是SOLID原则中的一部分。它的核心思想是软件实体(如类、模块、函数等)应该对扩展开放,对修改关闭。
具体来说,开闭原则要求设计的软件实体在新增功能时不需要修改原有的代码,而是通过扩展已有的代码来实现。这意味着应该通过接口、抽象类、继承、多态等方式,使得系统具有良好的可扩展性和灵活性。
开闭原则的关键是通过抽象来隔离变化,将变化的部分封装成抽象的接口或基类,使得对于实现的变化可以被独立地扩展,而不影响原有的代码。这样,系统可以很方便地进行功能的扩展和修改,同时又保持了稳定的接口和基础架构。
开闭原则具有以下优点:
可维护性:由于对现有代码的修改被限制在扩展的部分,所以系统的维护变得更加容易和安全。
可扩展性:通过扩展接口或基类,可以方便地添加新的功能,满足不同的需求。
可复用性:由于对现有代码的修改最小化,所以可以更好地复用原有的模块和功能。
遵循开闭原则可以使软件系统更稳定、可维护、可扩展,并能够适应变化和需求的不断演变。在实际开发中,我们通常通过使用设计模式、依赖倒置原则等方法来实现开闭原则。
某手机工厂专为各知名手机品牌代工生产各类手机,当需要苹果手机时只需要在调用该工厂的工厂方法时传人参数 iPhone,需要华为手机时只需要传人参数Huawei,工厂可以根据传入的不同参数返回不同品牌的手机。现使用简单工厂模式来模拟该工厂的生产过程。
以下是具体代码:
public interface Mobile {
public void work();
}
public class iPhone implements Mobile {
@Override
public void work() {
System.out.println("使用苹果手机");
}
}
public class Huawei implements Mobile {
@Override
public void work() {
System.out.println("使用华为手机");
}
}
/*
* MobileFactory 是工厂类,它是整个系统的核心,
* 它提供了静态工厂方法 produceMobile(),工厂方法中包含一个字符串类型的参数,
* 在内部业务逻辑中根据参数值的不同实例化不同的具体产品类,返回相应的对象。
* */
public class MobileFactory {
public static Mobile produceMobile(String brand) throws Exception {
if (brand.equals("iPhone")) {
System.out.println("工厂生产苹果手机");
return new iPhone();
} else if (brand.equals("Huawei")) {
System.out.println("工厂生产华为手机");
return new Huawei();
} else {
throw new Exception("暂无该品牌手机");
}
}
}
/*
* 在此引入了一个工具类——XMLUtilTV,通过它可以从 XMIL格式的配置文件中读取节点获取数据,
* 如品牌名称等信息,如果需要修改品牌名称,无须修改客户端代码,只需修改配置文件。
* 通过配置文件可以极大提高系统的扩展性,让软件实体更符合开闭原则。
* */
public class XMLUtilMobile {
public static String getBrandName() {
try {
DocumentBuilderFactory dBFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = dBFactory.newDocumentBuilder();
Document document;
document =builder.parse(new File("configMobile.xml"));
NodeList nl = document.getElementsByTagName("brandName");
Node classNode = nl.item(0).getFirstChild();
String brandName = classNode.getNodeValue().trim();
return brandName;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
public class Client {
public static void main(String[] args) {
try {
Mobile mobile;
String brandName = XMLUtilMobile.getBrandName();
mobile = MobileFactory.produceMobile(brandName);
mobile.work();
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
}
iPhone
如果需要更换手机品牌,无须修改客户端代码及类库代码,只需要修改配置文件即可,提高了系统的灵活性。如果需要增加新类型的手机,即增加新的具体产品类,则需要修改工厂类,这在一定程度上违反了开闭原则,但是无须修改客户端测试类,这就带来一定程度的灵活性。
1.简单工厂模式的优点
(1)工厂类含有必要的判断逻辑,可以决定在什么时候创建哪一个产品类的实例,客户端可以免除直接创建产品对象的责任,而仅仅“消费”产品;简单工厂模式通过这种做法实现了对责任的分割,它提供了专门的工厂类用于创建对象。
(2)客户端无须知道所创建的具体产品类的类名,只需要知道具体产品类所对应的参数即可,对于一些复杂的类名,通过简单工厂模式可以减少使用者的记忆量。
(3)通过引入配置文件,可以在不修改任何客户端代码的情况下更换和增加新的具体产品类,在一定程度上提高了系统的灵活性。
2.简单工厂模式的缺点
(1)由于工厂类集中了所有产品创建逻辑,一旦不能正常工作,整个系统都要受到影响。
(2) 使用简单工厂模式将会增加系统中类的个数,在一定程度上增加了系统的复杂度和理解难度。
(3)系统扩展困难,一旦添加新产品就不得不修改工厂逻辑,在产品类型较多时,有可能造成工厂逻辑过于复杂,不利于系统的扩展和维护。
(4)简单工厂模式由于使用了静态工厂方法,造成工厂角色无法形成基于继承的等级结构。