工厂模式

发布时间 2024-01-12 19:23:14作者: wangzhilei

简单工厂

  • 根据工厂类中方法传入的参数返回所需的对象。
  • 客户端(应用层)只知道传入工厂类的参数,对于如何创建对象逻辑不关心
  • 优点:只需要传入一个正确的参数,就可以获取你所需要的对象而无需知道其创建的细节
  • 缺点:工厂类的职责相对过重,增加新的产品需要修改工厂类的判断逻辑,违背开闭原则

coding:

 1 public interface IChat {
 2   void chatting(String seqNo);
 3 }
 4 
 5 public interface IChat {
 6   void chatting(String seqNo);
 7 }
 8 
 9 public interface IChat {
10   void chatting(String seqNo);
11 }
12 
13 public class ChatFactory1 {
14   public IChat createChat(String tools){
15     if("QQ".equals(tools)){
16       return new QQ();
17     }else if("WeiXin".equals(tools)) {
18       return new WeiXin();
19     }else{
20       return null;
21     }
22   }
23 }
24 
25 public class FactoryTest {
26 
27 public static void main(String[] args) {
28 // 1、使用单方法工厂模式进行测试
29   ChatFactory1 chatFactory1 = new ChatFactory1();
30   String seqNo = "1";
31   IChat weixin1 = chatFactory1.createChat("WeiXin");
32   weixin1.chatting(seqNo);
33   IChat qq1 = chatFactory1.createChat("QQ");
34   qq1.chatting(seqNo);
35   IChat momo = chatFactory1.createChat("MoMo");
36   if(momo == null){
37     System.out.println("创建陌陌聊天工具实例失败");
38   }else{
39     momo.chatting(seqNo);
40   }
41 
42  }
43 
44 }

UML

FactoryTest只创建ChatFactory工厂,QQ和WeiXin由ChatFactory工厂创建。缺点很明显,创建MoMo,就需要修改ChatFactory类,这不符合开闭原则。

反射方式改造

public class ChatFactory {
  public IChat createChat(Class c){
    IChat iChat = null;
    try {
      iChat =(IChat) Class.forName(c.getName()).getConstructor().newInstance();
    } catch (ClassNotFoundException e) {
      throw new RuntimeException(e);
    } catch (InvocationTargetException e) {
      throw new RuntimeException(e);
    } catch (InstantiationException e) {
      throw new RuntimeException(e);
    } catch (IllegalAccessException e) {
      throw new RuntimeException(e);
    } catch (NoSuchMethodException e) {
      throw new RuntimeException(e);
    }
    return iChat;
  }
}

工厂方法

  • 定义一个工厂接口(或抽象类),实现这个工厂接口的工厂类去决定实例化哪个类,一个工厂类只实例化一个类
  • 客户端(应用层)不依赖于产品类实例如何被创建、实现等细节
  • 一个类通过其子类类指定创建哪个对象
  • 优点:加入新产品符合开闭原则,提高可扩展性
  • 缺点:类的个数容易过来,增加复杂度,增加了系统的抽象性和理解难度

coding:

public interface IChat {
  void chatting(String seqNo);
}
/**
  三个实例对象
*/
public class WeiXinChat implements IChat {
  @Override
  public void chatting(String seqNo) {
    System.out.println("使用微信进行聊天 -- " + seqNo);
  }
}

public class QQChat implements IChat {
  @Override
  public void chatting(String seqNo) {
    System.out.println("使用QQ进行聊天 -- " + seqNo);
  }
}

public class QQChat implements IChat {
  @Override
  public void chatting(String seqNo) {
    System.out.println("使用QQ进行聊天 -- " + seqNo);
  }
}

/**
* 只制定规范契约,并不决定产生哪一种类的实例,产生哪一种实例完全交由子类实现
*/
public abstract class ChatFactory {
  public abstract IChat getChat();
}

public class WeiXinChatFactory extends ChatFactory{
  @Override
  public IChat getChat() {
    return new QQChat();
  }
}

public class QQChatFactory extends ChatFactory{
  @Override
  public IChat getChat() {
    return new QQChat();
  }
}

public class MoMoChatFactory extends ChatFactory{
  @Override
  public IChat getChat() {
    return new MoMoChat();
  }
}

public class Test {
  public static void main(String[] args) {
    ChatFactory weiXinChatFactory = new WeiXinChatFactory();
    ChatFactory qqChatFactory = new QQChatFactory();
    ChatFactory moMoChatFactory = new MoMoChatFactory();

    IChat chat = moMoChatFactory.getChat();
    chat.chatting("11");

  }
}

UML

可以看到三个实例工厂对应生产三个类的实例,Test测试类中要使用哪个实例,直接找对象的工厂实例get即可。

抽象工厂

  • 一个抽象工厂接口,包含一系列创建的接口方法,这些方法的返回是一个接口,意味着一个接口方法可以生产实现该接口的所有类对象。
  • 抽象工厂模式提供一个创建一系列相关或相互依赖对象的接口,无需指定他们具体的类
  • 使用场景:
    •   客户端(应用层)不依赖于产品类实例如何被创建、实现等细节
    •   强调一系列相关产品对象(属于同一产品族,产品族概念后面有解释)一起使用创建对象需要大量重复的代码
    •   提供一个产品类的库,所有的产品以同样的接口出现,从而使客户端不依赖于具体实现
  • 优点
    •   具体产品在应用层代码隔离,无需关心创建细节
    •   将一个系列的产品族统一到一起创建
  • 缺点
    •   规定了所有可能被创建的产品集合,产品族中扩展新的产品困难,需要修改抽象工厂的接口
    •   增加了系统的抽象性和理解难度

coding

  • 电脑抽象工厂接口 == 只提供电脑组件的组装,具体组装成什么样型号的电脑,再由具体工厂类决定
public interface IComputerFactory {
    IProduceKeyBord createKeyBord();
    IProduceMouse createMouse();
}
  • 键盘接口 -- 生产键盘
public interface IProduceKeyBord {
    /**
    * 约定键盘生产的规格数据
    * @param name 名称
    * @param color 颜色
    */
    void produceKeyboard(String name, String color);
}
  • 鼠标接口 -- 生产鼠标
public interface IProduceMouse {
    /**
    * 约定鼠标生产的规格数据
    * @param name 名称
    * @param type 类型
    */
    void produceMouse(String name,String type);
}
  • 雷柏(Rapoo)鼠标键盘生产商
public class LeiBoMouse implements IProduceMouse {
    @Override
    public void produceMouse(String name, String type) {
        System.out.println("雷柏鼠标 -- "+name+","+type);
    }
}

public class LeiBoKeyboard implements IProduceKeyBord {
    @Override
    public void produceKeyboard(String name, String color) {
        System.out.println("雷柏键盘 -- "+name+","+color);
    }
}    
  • 罗技(G)鼠标键盘生产商
public class LuoJiKeyboard implements IProduceKeyBord {
    @Override
    public void produceKeyboard(String name, String color) {
        System.out.println("罗技键盘 -- "+name+","+color);
    }
}

public class LuoJiMouse implements IProduceMouse {
    @Override
    public void produceMouse(String name, String type) {
        System.out.println("罗技鼠标 -- "+name+","+type);
    }
}        
  • 联想电脑生产商 == 电脑型号01【固定键盘和鼠标生产商】
public class LianXiang01Factory implements IComputerFactory {

    @Override
    public IProduceKeyBord createKeyBord() {
    // 使用雷柏的键盘
        return new LeiBoKeyboard();
    }

    @Override
    public IProduceMouse createMouse() {
    // 使用雷柏的鼠标
        return new LeiBoMouse();
    }
}            
  • 联想电脑生产商 == 电脑型号:02【固定键盘和鼠标生产商】
public class LianXiang02Factory implements IComputerFactory {

    @Override
    public IProduceKeyBord createKeyBord() {
    // 使用雷柏的键盘
        return new LeiBoKeyboard();
    }

    @Override
    public IProduceMouse createMouse() {
    // 使用罗技的鼠标
        return new LuoJiMouse();
    }
}    

抽象工厂测试 == 具体工厂类实现不同电脑组件的生产

public class AFactoryTest {

    public static void main(String[] args) {
    // 用户需要购买联想的电脑,但要求键盘和鼠标都是雷柏的,于是便找到了对应的01电脑组装工厂进行生产
        LianXiang01Factory lianXiang01Factory = new LianXiang01Factory();
        IProduceKeyBord keyboard1 = lianXiang01Factory.createKeyBord();
        keyboard1.produceKeyboard("M550", "黑色");
        IProduceMouse Mouse1 = lianXiang01Factory.createMouse();
        Mouse1.produceMouse("M590", "有线");

        System.out.println("=======================分割线,注意区别");

    // 用户需要购买联想的电脑,但要求键盘是雷柏的,鼠标是罗技的,于是便找到了对应的02电脑组装工厂进行生产
        LianXiang02Factory lianXiang02Factory = new LianXiang02Factory();
        IProduceKeyBord keyboard2 = lianXiang02Factory.createKeyBord();
        keyboard2.produceKeyboard("M550", "黑色");
        IProduceMouse Mouse2 = lianXiang02Factory.createMouse();
        Mouse2.produceMouse("M590", "无线");

    /**
    * 好处:横向扩展很容易,如果我需要再增加一个电脑型号的生产线,比如HP【惠普】,只需要在创建一个对应的工厂实现抽象工厂即可
    * 坏处:纵向扩展很麻烦,如果我需要增加显示器的生产接口,那么改动的地方就太多了。
    */
    }
}