工厂模式:用汉堡店的故事理解对象创建逻辑

67 阅读6分钟

一、故事解说:麦当劳如何制作汉堡

假设你去麦当劳点汉堡:

  1. 你不需要知道汉堡怎么做:告诉服务员 “我要一个巨无霸”,服务员不会让你自己去煎肉饼、烤面包,而是让后厨(工厂)制作。

  2. 后厨分工明确

    • 巨无霸工厂:专门做巨无霸(面包 + 双层肉饼 + 芝士);
    • 麦辣鸡腿堡工厂:专门做麦辣鸡腿堡(面包 + 炸鸡腿 + 生菜);
  3. 你只关心结果:拿到手的是包装好的汉堡,不用管中间步骤。

工厂模式核心:把对象的创建(做汉堡)和使用(吃汉堡)分离,客户端不用知道具体创建逻辑,只需告诉工厂要什么。

二、工厂模式核心角色(以汉堡为例)

  1. Product(产品) :汉堡接口,定义所有汉堡共有的方法(如getDescription());
  2. ConcreteProduct(具体产品) :巨无霸、麦辣鸡腿堡等具体汉堡;
  3. Factory(工厂) :定义创建汉堡的方法(如createHamburger());
  4. ConcreteFactory(具体工厂) :巨无霸工厂、麦辣鸡腿堡工厂,实现创建具体汉堡的逻辑。

三、Android 常用工厂模式案例与实现

案例 1:View 的创建(LayoutInflater 工厂)

java

// Product:View接口(Android中View是抽象类)
public abstract class View {
    // View的共同方法
    public abstract void onClick();
    public abstract void setText(String text);
    // 省略其他方法...
}

// ConcreteProduct:具体View
public class Button extends View {
    private String text;
    
    @Override
    public void onClick() {
        Log.d("Button", "Clicked: " + text);
    }
    
    @Override
    public void setText(String text) {
        this.text = text;
    }
}

public class TextView extends View {
    private String text;
    
    @Override
    public void onClick() {
        Log.d("TextView", "TextView can't be clicked");
    }
    
    @Override
    public void setText(String text) {
        this.text = text;
        Log.d("TextView", "Text set: " + text);
    }
}

// Factory:LayoutInflater(Android中的视图工厂)
public class LayoutInflater {
    private Context context;
    
    private LayoutInflater(Context context) {
        this.context = context;
    }
    
    // 简单工厂模式:根据类型创建View
    public static View createView(Context context, String viewType) {
        if ("button".equals(viewType)) {
            return new Button();
        } else if ("textview".equals(viewType)) {
            return new TextView();
        } else {
            throw new IllegalArgumentException("Unknown view type: " + viewType);
        }
    }
    
    // 工厂方法模式:子类实现具体创建逻辑(Android中通过子类实现)
    public View inflate(int layoutResId) {
        // 实际Android中会解析XML布局,创建对应View
        // 简化示例:假设layoutResId对应不同View
        if (layoutResId == R.layout.button_layout) {
            return new Button();
        } else if (layoutResId == R.layout.textview_layout) {
            return new TextView();
        }
        return null;
    }
}

// 使用示例(类似Android中LayoutInflater的用法)
View button = LayoutInflater.createView(context, "button");
button.setText("点击我");
button.onClick(); // 输出:Clicked: 点击我

优点

  • 解耦创建与使用:Activity 不用知道 Button 如何创建,只需调用工厂方法;

  • 扩展性好:新增 View 类型(如 EditText)只需修改工厂,客户端代码不变;

  • 符合 Android 设计:Android 系统大量使用此模式,如LayoutInflater.from(context).inflate(...)

缺点

  • 简单工厂违反开闭原则:新增 View 类型需要修改createView方法;
  • 类型安全依赖字符串:通过字符串("button")指定类型,可能因拼写错误导致异常。

案例 2:Adapter 创建(RecyclerView.Adapter 工厂)

java

// Product:Adapter接口
public abstract class Adapter {
    public abstract int getItemCount();
    public abstract View onCreateViewHolder(ViewGroup parent, int viewType);
    public abstract void onBindViewHolder(ViewHolder holder, int position);
}

// ConcreteProduct:具体Adapter
public class UserAdapter extends Adapter {
    private List<User> users;
    
    public UserAdapter(List<User> users) {
        this.users = users;
    }
    
    @Override
    public int getItemCount() {
        return users.size();
    }
    
    @Override
    public View onCreateViewHolder(ViewGroup parent, int viewType) {
        // 创建Item View
        return LayoutInflater.createView(parent.getContext(), "textview");
    }
    
    @Override
    public void onBindViewHolder(ViewHolder holder, int position) {
        // 绑定数据
    }
}

public class PhotoAdapter extends Adapter {
    private List<Photo> photos;
    
    public PhotoAdapter(List<Photo> photos) {
        this.photos = photos;
    }
    
    @Override
    public int getItemCount() {
        return photos.size();
    }
    
    @Override
    public View onCreateViewHolder(ViewGroup parent, int viewType) {
        // 创建不同的Item View
        return LayoutInflater.createView(parent.getContext(), "imageview");
    }
    
    @Override
    public void onBindViewHolder(ViewHolder holder, int position) {
        // 绑定图片数据
    }
}

// Factory:AdapterFactory
public class AdapterFactory {
    // 工厂方法模式:根据数据类型创建对应的Adapter
    public static Adapter createAdapter(List data) {
        if (data instanceof List<User>) {
            return new UserAdapter((List<User>) data);
        } else if (data instanceof List<Photo>) {
            return new PhotoAdapter((List<Photo>) data);
        } else {
            throw new IllegalArgumentException("Unsupported data type");
        }
    }
}

// 使用示例
List<User> users = getUsersFromServer();
Adapter userAdapter = AdapterFactory.createAdapter(users);
recyclerView.setAdapter(userAdapter);

优点

  • 数据与 UI 解耦:Adapter 负责数据展示,工厂负责创建 Adapter,Activity 只需传入数据;

  • 多类型适配:不同数据类型(用户列表、图片列表)对应不同 Adapter,工厂自动匹配;

  • 符合 Android 规范:RecyclerView 强制要求使用 Adapter,工厂模式简化创建流程。

缺点

  • 类型检查依赖运行时instanceof判断在运行时进行,编译时无法发现类型错误;
  • 工厂逻辑可能复杂:当 Adapter 类型过多时,工厂的createAdapter方法会变得臃肿。

案例 3:系统服务获取(Context 的工厂方法)

java

// Product:系统服务接口(Android中以接口或抽象类存在)
public interface SystemService {
    void start();
    void stop();
}

// ConcreteProduct:具体系统服务
public class NotificationService implements SystemService {
    @Override
    public void start() {
        Log.d("NotificationService", "Notification service started");
    }
    
    @Override
    public void stop() {
        Log.d("NotificationService", "Notification service stopped");
    }
}

public class NetworkService implements SystemService {
    @Override
    public void start() {
        Log.d("NetworkService", "Network service started");
    }
    
    @Override
    public void stop() {
        Log.d("NetworkService", "Network service stopped");
    }
}

// Factory:Context(Android中的系统服务工厂)
public class Context {
    // 简化的系统服务获取方法(类似Android的getSystemService)
    public SystemService getSystemService(String serviceType) {
        if (Context.NOTIFICATION_SERVICE.equals(serviceType)) {
            return new NotificationService();
        } else if (Context.NETWORK_SERVICE.equals(serviceType)) {
            return new NetworkService();
        } else {
            throw new IllegalArgumentException("Unknown service: " + serviceType);
        }
        
        // Android中实际返回的是系统服务的代理对象
    }
    
    // 常量定义(类似Android的Context类)
    public static final String NOTIFICATION_SERVICE = "notification";
    public static final String NETWORK_SERVICE = "network";
}

// 使用示例(类似Android中获取系统服务)
Context context = getAppContext();
SystemService notificationService = context.getSystemService(Context.NOTIFICATION_SERVICE);
notificationService.start(); // 输出:Notification service started

优点

  • 统一服务获取接口:所有系统服务通过getSystemService获取,客户端无需知道服务实现细节;

  • 隐藏复杂逻辑:系统服务的创建、初始化、权限检查等逻辑封装在工厂内部;

  • 符合单例模式:Android 中系统服务实际是单例,工厂模式可方便实现单例(示例中未体现,实际会缓存服务实例)。

缺点

  • 字符串类型安全问题:使用字符串(如 "notification")获取服务,拼写错误会导致异常;
  • 工厂职责过重:Context 类除了创建服务,还有其他大量职责,违反单一职责原则。

四、工厂模式的适用场景与总结

适用场景

  1. 对象创建逻辑复杂:如 View 需要初始化、设置属性,工厂封装这些步骤;
  2. 需要解耦创建与使用:客户端只关心 “用什么”,不关心 “怎么造”;
  3. 多类型产品管理:如 Adapter 有多种类型,工厂根据数据自动匹配;
  4. 系统服务获取:如 Android 的getSystemService,隐藏服务创建细节。

核心优点

  • 解耦代码:客户端与具体产品解耦,代码更灵活;
  • 扩展性好:新增产品类型只需扩展工厂,符合开闭原则;
  • 封装复杂性:隐藏对象创建的复杂逻辑(如 View 的布局解析、服务的权限检查)。

核心缺点

  • 类数量增加:每个产品需要对应的工厂和类,小型项目可能过度设计;
  • 工厂逻辑可能臃肿:简单工厂模式中,工厂方法可能包含大量if-else
  • 类型安全依赖运行时:通过字符串或instanceof判断类型,编译时无法校验。

Android 中的最佳实践

  • 优先使用系统工厂:如LayoutInflaterContext.getSystemService,避免重复造轮子;

  • 自定义工厂时

    1. 用枚举替代字符串(如ServiceType.NOTIFICATION),避免拼写错误;
    2. 结合泛型减少类型转换(如AdapterFactory.<User>createAdapter(users));
    3. 复杂场景使用工厂方法模式(每个产品对应一个工厂类),避免单一工厂过度复杂。