设计模式之工厂模式

  • 代码不完全参照原书 , 借鉴书中相关例子和部分概念
  • 顺序部分参考原书,部分引用原书 UML 类图,会结合实际应用描述优缺点
  • 工厂模式(简单工厂模式,工厂方法模式,抽象工厂模式)

工厂模式

概念理解

  • 工厂:主要负责对象的创建。其中涉及到的对象一般都继承自同一接口同一父类,具有部分相似的性质。

实例讲解

场景:
  • 目标:选择通讯方式实现发送消息的操作
  • 分析:发送消息的方式在我们日常生活中有很多种,比较常见的方式有
    • 短信(Message,SMS)
    • 邮件(EMail)
    • 微信等网络即时通讯工具 (Wechat)
常规实现:
  • 思路:通过用户选择的操作进行相关类型的传递,利用 If-Else 或者 Swicth-Case 语句来进行判断,针对每一种方式进行具体实现。
  • 代码实现:
import java.util.Scanner;

public class SingleClassImpl {

    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        System.out.println("Please enter receiver name:");
        String receiverName = in.next();
        System.out.println("Please choose one type to send msg:[Enter numbers]");
        System.out.println("1. Send by message app on your phone.");
        System.out.println("2. Send by email");
        System.out.println("3. Send by WeChat");
        System.out.println();
        int typeNo = 0;
        try {
            typeNo = in.nextInt();
        } catch (Exception e) {
            // handle exception
            System.out.println("Invalid input!");
        }
        switch (typeNo) {
            case 1:
                System.out.println(String.format("Send to %s by message", receiverName));
                break;
            case 2:
                System.out.println(String.format("Send to %s by email", receiverName));
                break;
            case 3:
                System.out.println(String.format("Send to %s by WeChat", receiverName));
                break;
            default:
                System.out.println("Invalid Input!");
                break;
        }
    }
}
  • 存在的问题
      1. 没有使用面向对象的特性。
      1. 如果我们需要添加新的发送消息的方式,则需要修改该类大量的业务逻辑,最主要的问题则是 和用户交互的逻辑同业务代码耦合度较高。修改代码增加了风险。
简单工厂模式:
  • 思路:
      1. 充分利用面向对象的特性,将所有发送消息的方式进行抽象,建立发送消息的接口类,定义共同的 sendMsg() 方法。
      1. 新建一个Factory类来管理各种信息发送方式。
  • 代码实现(一):Common Interface 和具体的 Sender
// common interface
public interface CommonInterfaceSender {
	void sendMsg(String receiver);
}

// SmsSender class(Message app in phone)
public class SmsSender implements CommonInterfaceSender {
    @Override
    public void sendMsg(String receiver) {
        System.out.println(String.format("Send to %s by message", receiver));
    }
}

// MailSender class
public class MailSender implements CommonInterfaceSender {
    @Override
    public void sendMsg(String receiver) {
        System.out.println(String.format("Send to %s by email", receiver));
    }
}

// WeChatSender class
public class WechatSender implements CommonInterfaceSender {
    @Override
    public void sendMsg(String receiver) {
        System.out.println(String.format("Send to %s by WeChat", receiver));
    }
}
  • 代码实现(二):Factory ClassMain Class
// Create different sender class instance according to param
public class OrdinaryFactory {
    public CommonInterfaceSender sendMsg(int type) {
        if (1 == type) {
            return new SmsSender();
        } else if (2 == type) {
            return new MailSender();
        } else if (3 == type) {
            return new WechatSender();
        } else {
            System.out.println("Please check the input!");
            return null;
        }
    }
}

public class Main {
    public static void main(String[] args) {
        OrdinaryFactory ordinaryFactory = new OrdinaryFactory();
        Scanner in = new Scanner(System.in);
        System.out.println("Please enter receiver name:");
        String receiverName = in.next();
        System.out.println("Please choose one type to send msg:[Enter numbers]");
        System.out.println("1. Send by message app on your phone.");
        System.out.println("2. Send by email");
        System.out.println("3. Send by WeChat");
        System.out.println();
        int typeNo = 0;
        try {
            typeNo = in.nextInt();
        } catch (Exception e) {
            // handle exception
            System.out.println("Invalid input!");
        }
        
        // Use parent interface to point the object.
        CommonInterfaceSender sender = ordinaryFactory.sendMsg(typeNo);
        // invoke parent interface method.
        sender.sendMsg(receiverName);
    }
}
  • 优点:充分发挥了面向对象的特点,使得具体的业务代码一定程度上和 Main 解耦,添加新的发送短信的方式时,只需要新建对应的 Sender,并简单调整对应的 Factory
  • 缺点
    • 严格意义上并没有实现完全解耦。客户端(Main)和业务逻辑代码中仍然存在依赖,Factory起了决定作用。同时对于传递的参数具有依赖性,参数的不合法导致程序运行的不合法。
    • 违反了 开放-封闭原则。需要调整 Factory 的判断逻辑,对修改开放。

开放-封闭原则:软件实体应该是可扩展,而不可修改的。也就是说,对扩展是开放的,而对修改是封闭的。

工厂方法模式
  • 思路:通过为每一个Sender Class创建Factory类,来代替简单工厂模式中Factory Class的作用,将判断的逻辑(实例化子类的逻辑)由Factory转移到客户端。
  • 代码实现(一):工厂类
// common factory
public interface IMsgFactory {
	CommonInterfaceSender sendMsg();
}

// Mail factory
public class SendMailFactory implements IMsgFactory {
	@Override
	public CommonInterfaceSender sendMsg() {
		return new MailSender();
	}
}

// Sms factory
public class SendSmsFactory implements IMsgFactory {
	@Override
	public CommonInterfaceSender sendMsg() {
		return new SmsSender();
	}
}

// WeChat factory
public class WechatFactory implements IMsgFactory {
    @Override
    public CommonInterfaceSender sendMsg() {
        return new WechatSender();
    }
}
  • 代码实现(二):客户端 Main
public class FactoryMethodMain {
    public static void main(String[] args) {
    
        // Choose one specific class to create factory referring demand
        IMsgFactory factory = new SendMailFactory();
        //IMsgFactory factory = new SendSmsFactory();
        //IMsgFactory factory = new WechatFactory();
        CommonInterfaceSender sender = factory.createSender();
        sender.sendMsg("Elvis");
    }
}
  • 优点:解决了简单工厂模式中的开放-封闭原则问题。通过客户端选择实例化哪一个具体的工厂类来达到最少修改代码的目的。同时保持了简单工厂模式面向对象的优点。
  • 缺点:本质上仍然没有解决选择的问题,当有新的需求的时候,客户端的代码仍然需要修改。除此之外,增加了代码量,引进一个新的发送消息的方式时,还需要建立对应的工厂类。
  • Tips:反射解决分支判断的问题。类比于Spring IOC容器中根据 beanName 注入相应的实例
抽象工厂模式
  • 使用场景:多个属性形成一组属性(一套属性),需要对一组属性中的某一个属性进行消费时
  • 结合本例:各种通信方式可能还会有接受消息的功能。
  • 代码实现(一):Receivers
public interface CommonInterfaceReceiver {
    void receiveMsg(String from);
}

public class MailReceiver implements CommonInterfaceReceiver {
    @Override
    public void receiveMsg(String from) {
        System.out.println(String.format("Receive message from %s by email", from));
    }
}

public class SmsReceiver implements CommonInterfaceReceiver {
    @Override
    public void receiveMsg(String from) {
        System.out.println(String.format("Receive message form %s by sms", from));
    }
}

public class WechatReceiver implements CommonInterfaceReceiver {
    @Override
    public void receiveMsg(String from) {
        System.out.println(String.format("Receive message from %s by wechat", from));
    }
}
  • 代码实现(二):建立抽象工厂类并根据功能提供各自的实现
public abstract class AbstractCommunicationFactory {
    public abstract CommonInterfaceSender getSender(int typeNum);
    public abstract CommonInterfaceReceiver getReceiver(int typeNum);
}

public class ReceiverFactory extends AbstractCommunicationFactory {
    @Override
    public CommonInterfaceSender getSender(int typeNum) {
        return null;
    }

    @Override
    public CommonInterfaceReceiver getReceiver(int typeNum) {
        switch (typeNum) {
            case 0:
                return new SmsReceiver();
            case 1:
                return new MailReceiver();
            case 2:
                return new WechatReceiver();
            default:
                return null;
        }
    }
}

public class SenderFactory extends AbstractCommunicationFactory {

    @Override
    public CommonInterfaceSender getSender(int typeNum) {
        switch (typeNum) {
            case 0:
                return new SmsSender();
            case 1:
                return new MailSender();
            case 2:
                return new WechatSender();
            default:
                return null;
        }
    }

    @Override
    public CommonInterfaceReceiver getReceiver(int typeNum) {
        return null;
    }
}
  • 代码实现(三):利用简单工厂模式对具体的功能对应的工厂进行管理
ublic class FactoryProducer {

    public static AbstractCommunicationFactory getFactory(String direction) {
        if ("Send".equals(direction)) {
            return new SenderFactory();
        } else if ("Receive".equals(direction)) {
            return new ReceiverFactory();
        }
        return null;
    }
}
  • 代码实现(四):Main
public class AbstractFactoryMain {

    public static void main(String[] args) {
        String to = "Elvis";
        String from  = "Shunzi";
        AbstractCommunicationFactory senderFactory = FactoryProducer.getFactory("Send");

        // sms sender
        CommonInterfaceSender smsSender = senderFactory.getSender(0);
        smsSender.sendMsg(to);

        // MailSender
        CommonInterfaceSender mailSender = senderFactory.getSender(1);
        smsSender.sendMsg(to);

        // WeChat Sender
        CommonInterfaceSender wechatSender = senderFactory.getSender(2);
        wechatSender.sendMsg(to);

        AbstractCommunicationFactory receiverFactory = FactoryProducer.getFactory("Receive");

        // sms receiver
        CommonInterfaceReceiver smsReceiver = receiverFactory.getReceiver(0);
        smsReceiver.receiveMsg(from);

        CommonInterfaceReceiver mailReceiver = receiverFactory.getReceiver(1);
        mailReceiver.receiveMsg(from);

        CommonInterfaceReceiver wechatReceiver = receiverFactory.getReceiver(2);
        wechatReceiver.receiveMsg(from);

    }
}
  • 输出结果:
> Send to Elvis by message
> Send to Elvis by message
> Send to Elvis by WeChat
> Receive message form Shunzi by sms
> Receive message from Shunzi by email
> Receive message from Shunzi by wechat
  • 优点:便于修改对应的业务逻辑,具体的工厂类实现只需要一行代码,其余操作都是抽象工厂的继承,减少代码修改的成本。同时让创建具体对象的逻辑和客户端分离,通过实例化对应的抽象工厂类来实现。
  • 缺点
    • 仍然不可避免地违反了开放-封闭原则,需要在客户端中根据不同的业务需求调整实例化工厂的代码。可以采用反射解决
    • 抽象工厂在扩展相关功能的基础上成本较高,譬如新建拒收消息的功能,则需要按照接受和发送消息的类结构再次新建对应的类,并修改工厂中的部分逻辑。