简单介绍一下UML:
泛化: 继承 带三角箭头的实线,箭头指向类
实现: 实现 带三角箭头的虚线,箭头指向接口
依赖: new A的对象当作方法参数传递进来作为B类的局部变量 带箭头的虚线,指向被使用者
关联: 一个类作为另一个类的成员变量 带普通箭头的实心线,指向被拥有者
聚合: new A的对象当作方法参数传递进来作为B类的成部变量 带空心菱形的实心线,菱形指向整体
组合: new A的对象当作构造方法参数传递进来作为B类的成部变量或者A类作为B类成 员变量并已经new A(A类和B类具有相同的生命周期) 带实心菱形的实线,菱形指向整体 总结:各种关系的强弱顺序:泛化 = 实现 > 组合 > 聚合 > 关联 > 依赖
区分:
UML说明: 苹果手机和红米手机继承了手机这个抽象类,工厂类里根据客户端传入的参数生成相应的对象,如,客户说要红米,工厂给客户一个红米手机,客户说要苹果,工厂给客户一个苹果手机。
简单工厂有三个对象:
public interface Phone {
void produce();
}
具体产品类
public class ApplePhoneImpl implements Phone{
@Override
public void produce() {
System.out.println("生产苹果手机");
}
}
public class RedmiPhoneImpl implements Phone{
@Override
public void produce() {
System.out.println("生产了红米手机");
}
}
工厂类
public class Factory {
public Phone getPhone(String type){
Phone phone = null;
if("红米".equals(type)){
phone = new RedmiPhoneImpl();
}else if("苹果".equals(type)){
phone = new ApplePhoneImpl();
}//.....
return phone;
}
}
测试
@Test
public void test1(){
Factory factory = new Factory();
Phone redmiPhone = factory.getPhone("红米");
System.out.println(redmiPhone);
redmiPhone.produce();
Phone applePhone = factory.getPhone("苹果");
System.out.println(applePhone);
applePhone.produce();
}
运行结果如下:
通过反射创建对象,以改进了之前提到的缺点(增加新的产品需要修改工厂类的判断逻辑),现在增加新的具体产品的时候不需要修改工厂中的代码。满足了开闭原则。
工厂类代码如下
public class FactoryPlus {
public Phone getPhone(Class clazz) throws Exception {
return (Phone) Class.forName(clazz.getName()).newInstance();
}
}
测试代码如下:
@Test
public void test2() throws Exception {
FactoryPlus factory = new FactoryPlus();
Phone redmiPhone = factory.getPhone(RedmiPhoneImpl.class);
System.out.println(redmiPhone);
redmiPhone.produce();
Phone applePhone = factory.getPhone(ApplePhoneImpl.class);
System.out.println(applePhone);
applePhone.produce();
}
运行结果如下:
工厂类中的方法逻辑,是利用反射机制生成对象返回,好处是增加一种产品时,不需要修改工厂类中的代码。满足了开闭原则。缺点:这种写法粗看牛逼,细想之下,不谈reflection的效率还有以下问题:个人觉得不好,因为Class.forName(clz.getName()).newInstance()调用的是无参构造函数生成对象,它和newObject()是一样的性质,而工厂方法应该用于复杂对象的初始化 ,当需要调用有参的构造函数时便无能为力了,这样像为了工厂而工厂,没有实际意义。2 不同的产品需要不同额外参数的时候 不支持。
工厂类
public class FactoryPlusPlus {
/**
* 熟悉吧!!!spring ioc 就是通过将下面的这句话配置在配置文件中,再利用反射创建对象,
* 这就是spring ioc的原理:工厂+配置文件+反射!!以达到彻底解耦的目的**/
private static String className="com.wander.design.simplefactory.product.ApplePhoneImpl";
public static Phone getPhone() throws Exception {
return (Phone) Class.forName(className).newInstance();
}
}
测试代码
@Test
public void test3() throws Exception {
Phone phone = FactoryPlusPlus.getPhone();
phone.produce();
}
说明
spring ioc容器的原理就是这种方式:工厂+配置文件+反射,spring通过读取配置文件(),获取到className再利用反射机制Class.forName(className).newInstance()得到对象赋值给配置文件里bean标签的id属性的值,就是工厂生成的对象名。
优点: 就是满足OCP原则,在不修改源代码的前提下切换底层的实现,达到解耦的目的!
使用以上两种方法的工厂,都有两个缺点:一是不同的产品需要不同额外参数的时候不支持。二是如果使用时传递的type、Class出错,将不能得到正确的对象,容错率不高。
而多方法的工厂模式为不同产品,提供不同的生产方法,使用时 需要哪种产品就调用该种产品的方法,使用方便、容错率高。
工厂类代码如下:
public class FactoryMoreMethod {
public static Phone getApple(){
return new ApplePhoneImpl();
}
public static Phone getRedmi(){
return new RedmiPhoneImpl();
}
/**新增华为手机产品,只需要在工厂中增加一个静态方法即可,不需要修改原有的方法**/
public static Phone getHonor(){
return new HonorPhoneImpl();
}
}
测试代码
@Test
public void test3() throws Exception {
Phone apple = FactoryMoreMethod.getApple();
apple.produce();
Phone redmi = FactoryMoreMethod.getRedmi();
redmi.produce();
Phone honor = FactoryMoreMethod.getHonor();
honor.produce();
}
应用场景
查看java源码:java.util.concurrent.Executors类便是一个生成Executor 的工厂 ,其采用的便是 多方法静态工厂模式:例如ThreadPoolExecutor类构造方法有5个参数,其中三个参数写法固定,前两个参数可配置,如下写。
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
又如JDK想增加创建ForkJoinPool类的方法了,只想配置parallelism参数,便在类里增加一个如下的方法:
public static ExecutorService newWorkStealingPool(int parallelism) {
return new ForkJoinPool
(parallelism,
ForkJoinPool.defaultForkJoinWorkerThreadFactory,
null, true);
}
总结:多方法工厂的优势,方便创建同种类型的复杂参数对象。
Calendar cal = Calendar.getInstance(zone.toTimeZone(), locale);
public static Calendar getInstance(TimeZone zone, Locale aLocale) {
return createCalendar(zone, aLocale);
}
private static Calendar createCalendar(TimeZone zone, Locale aLocale) {
'部分删减'
Calendar cal = null;
if (aLocale.hasExtensions()) {
String caltype = aLocale.getUnicodeLocaleType("ca");
if (caltype != null) {
switch (caltype) {
case "buddhist":
cal = new BuddhistCalendar(zone, aLocale);
break;
case "japanese":
cal = new JapaneseImperialCalendar(zone, aLocale);
break;
case "Gregory":
cal = new GregorianCalendar(zone, aLocale);
break;
}
}
}
if (cal == null) {
if (aLocale.getLanguage() == "th" && aLocale.getCountry() == "TH") {
cal = new BuddhistCalendar(zone, aLocale);
} else if (aLocale.getVariant() == "JP" && aLocale.getLanguage() == "ja"
&& aLocale.getCountry() == "JP") {
cal = new JapaneseImperialCalendar(zone, aLocale);
} else {
cal = new GregorianCalendar(zone, aLocale);
}
}
return cal;
}
UML说明: 苹果手机和红米手机实现了手机这个抽象类,苹果工厂和红米工厂实现了抽象工厂,苹果工厂当然要生产(依赖)苹果手机,红米工厂当然要生产(依赖)红米。客户要买苹果手机要去问苹果工厂要苹果手机,客户要买红米手机当然要去问红米工厂要红米手机。
工厂方法有四个对象:
(1)抽象产品类和简单工厂的抽象产品类一样
(2)具体产品类和简单工厂的具体产品类一样
(3)抽象工厂
public interface Factory {
Phone getPhone();
}
具体工厂
public class AppleFactoryImpl implements Factory{
@Override
public Phone getPhone() {
return new ApplePhoneImpl();
}
}
public class RedmiFactoryImpl implements Factory{
@Override
public Phone getPhone() {
return new RedmiPhoneImpl();
}
}
测试代码
@Test
public void test1(){
Factory applePhoneFactory = new AppleFactoryImpl();
Factory redmiPhoneFactory = new RedmiFactoryImpl();
Phone applePhone = applePhoneFactory.getPhone();
Phone redmiPhone = redmiPhoneFactory.getPhone();
System.out.println(applePhone);
System.out.println(redmiPhone);
applePhone.produce();
redmiPhone.produce();
}
优点:
①用户只需要关心所需产品的对应工厂,无需关心细节
②完全支持开闭原则,提高可扩展性。所谓的开闭原则就是对扩展开放,对修改关闭,再说白点就是实现工厂方法以后要进行扩展时不需要修改原有代码,只需要增加一个工厂实现类和产品实现类就可以。这样的好处可以降低因为修改代码引进错误的风险。
缺点:
①每加入一种产品,会创建一个具体工厂类和具体产品类,因此,类的个数容易过多,增加复杂度。
②抽象工厂和抽象产品增加了系统的抽象性和理解难度
①可以看出,工厂方法模式特点:不仅仅做出来的产品要抽象, 工厂也应该需要抽象。
②工厂方法使一个产品类的实例化延迟到其具体工厂子类.
③工厂方法的好处就是更拥抱变化。当需求变化,只需要增删相应的类,不需要修改已有的类。
④而简单工厂需要修改工厂类的方法,多方法静态工厂模式需要增加一个静态方法。
缺点:引入抽象工厂层后,每次新增一个具体产品类,也要同时新增一个具体工厂类,所以我更青睐多方法静态工厂,每次新增一个具体产品类,工厂只需要新增一个静态方法。
(1)客户端不知道它所需要的对象的类。(需要知道所需的对象的类使用升级版简单工厂模式,需要知道所需的参数的类使用简单工厂模式)。
(2)抽象工厂类通过其子类来指定创建哪个对象。
(3)简单工厂在源码中的使用–Collection:Collection(抽象工厂):
public interface Collection<E> extends Iterable<E> {
Iterator<E> iterator();
}
ArrayList(具体工厂):
public class ArrayList<E>{
public Iterator<E> iterator() {
return new Itr();
}
}
Iterator(抽象产品):
public interface Iterator<E> {
boolean hasNext();
}
Itr(具体产品):
private class Itr implements Iterator<E> {
int cursor; // index of next element to return
int lastRet = -1; // index of last element returned; -1 if no such
int expectedModCount = modCount;
public boolean hasNext() {
return cursor != size;
}
'省略代码...'
}
①一句话来说就是,创建相关或依赖对象的家族,而无需明确指定具体类。因为我们可以定义具体产品类实现不止一个抽象工厂接口,一个工厂也可以生成不止一个产品类,是三个模式中较为抽象,并具一般性的模式。我们在使用中要注意使用抽象工厂模式的条件。
②所谓抽象工厂模式就是提供一个接口,用于创建相关或者依赖对象的家族,而不需要明确指定具体类。他允许客户端使用抽象的接口来创建一组相关的产品,而不需要关心实际产出的具体产品是什么。这样一来,客户就可以从具体的产品中被解耦。它的优点是隔离了具体类的生成,使得客户端不需要知道什么被创建了,而缺点就在于新增新的行为会比较麻烦,因为当添加一个新的产品对象时,需要更改接口及其下所有子类。
UML说明: 具体的苹果手机产品和具体的红米手机产品实现了手机产品抽象类,具体的苹果充电器产品和具体的红米充电器产品实现了充电器产品抽象类。具体的苹果工厂和具体的红米工厂实现了手机抽象工厂,然后苹果工厂生产苹果手机和苹果充电器,红米工厂生成红米手机和红米充电器。客户想要苹果手机和苹果充电器就要向苹果工厂要产品(对象),客户想要红米手机和红米充电器就要向红米工厂要产品(对象)。
工厂方法有四个对象:
抽象的产品
public interface Phone {
void produce();
}
public interface Charger {
void produce();
}
具体的产品
public class AppleChargerImpl implements Charger{
@Override
public void produce() {
System.out.println("生产苹果充电器");
}
}
public class ApplePhoneImpl implements Phone {
@Override
public void produce() {
System.out.println("生产苹果手机");
}
}
public class RedmiChargerImpl implements Charger{
@Override
public void produce() {
System.out.println("生产红米充电器");
}
}
public class RedmiPhoneImpl implements Phone {
@Override
public void produce() {
System.out.println("生产了红米手机");
}
}
public interface Factory {
Phone getPhone();
Charger getCharger();
}
public class AppleFactoryImpl implements Factory {
@Override
public Phone getPhone() {
return new ApplePhoneImpl();
}
@Override
public Charger getCharger() {
return new AppleChargerImpl();
}
}
public class RedmiFactoryImpl implements Factory {
@Override
public Phone getPhone() {
return new RedmiPhoneImpl();
}
@Override
public Charger getCharger() {
return new RedmiChargerImpl();
}
}
@Test
public void test1(){
Factory appleFactory = new AppleFactoryImpl();
Phone applePhone = appleFactory.getPhone();
Charger appleCharger = appleFactory.getCharger();
System.out.println(appleFactory);
applePhone.produce();
appleCharger.produce();
Factory redmiFactory = new RedmiFactoryImpl();
Phone redmiPhone = redmiFactory.getPhone();
Charger redmiCharger = redmiFactory.getCharger();
System.out.println(redmiFactory);
redmiPhone.produce();
redmiCharger.produce();
}
执行结果
优点:
缺点:
抽象工厂在实际的开发中运用并不多,主要是在开发工程中很少会出现多个产品种类的情况,大部分情况使用以上两种工厂模式即可解决
一句话总结工厂模式:方便创建 同种产品类型的 复杂参数 对象工厂模式重点就是适用于 构建同产品类型(同一个接口 基类)的不同对象时,这些对象new很复杂,需要很多的参数,而这些参数中大部分都是固定的,so,懒惰的程序员便用工厂模式封装之。(如果构建某个对象很复杂,需要很多参数,但这些参数大部分都是“不固定”的,应该使用建造者Builder模式)