面试知识点九:设计模式
88.说一下你熟悉的设计模式?
89.简单工厂和抽象工厂有什么区别?
88.说一下你熟悉的设计模式?
参考:设计模式总结
1、单例模式:单例模式保证一个类仅有一个实例,同时这个类还必须提供一个访问该类的全局访问点。作用就是保证在整个应用程序的生命周期中,任何一个时刻,单例类的实例都只存在一个(当然也可以不存在)。
单例模式的结构图
从上面的类图中可以看出,在单例类中有一个构造函数 Singleton ,但是这个构造函数却是私有的(前面是“ - ”符号),然后在里面还公开了一个 GetInstance()方法。
根据上述描述,可以得到如下代码:
public class Singleton { private static Singleton instance; /* * 构造函数私有,不能通过其创建对象 * */ private Singleton(){ } /* * 提供一个获取对象的公共方法 * */ public static Singleton getInstance(){ if(instance == null){ instance = new Singleton(); } return instance; } }
测试一下:
public class Test { public static void main(String[] args) { Singleton instance1 = Singleton.getInstance(); Singleton instance2 = Singleton.getInstance(); System.out.println(instance1 == instance2); } }
结果:
true
可以看到,获取的是同一个实例。
完善一下,多线程情况下实现单例模式。先模拟一下上述代码中多线程下的问题:
public class Singleton { private static Singleton instance; private Singleton(){ } public static Singleton getInstance(){ if(instance == null){ try { Thread.sleep(10); instance = new Singleton(); } catch (InterruptedException e) { e.printStackTrace(); } } return instance; } }
public class Thread01 extends Thread{ @Override public void run() { System.out.println(Singleton.getInstance().hashCode()); } }
测试一下:
public class Test { public static void main(String[] args) { Thread[] threads = new Thread[5]; for (int i = 0; i < threads.length; i++) { threads[i] = new Thread01(); } for (int i = 0; i < threads.length; i++) { threads[i].start(); } } }
结果:
2057546272 1311791352 1892782834 528123358 1996397762
可以看到,上述代码在多线程模式下单例模式存在问题,返回的可能不再是同一个实例对象。解决方法,if条件判断加锁
public class Singleton { private static Singleton instance; private static Object synchObject = new Object(); private Singleton(){ } public static Singleton getInstance(){ synchronized (synchObject){ if(instance == null){ instance = new Singleton(); } } return instance; } }
其他不变,测试结果:
156522743 156522743 156522743 156522743 156522743
单例模式多线程下获取的也是同一个对象。例子中的是懒汉式单例模式,还有饿汉式单例模式。
89.简单工厂和抽象工厂有什么区别?
代码演示:
定义一个Car接口:
public interface Car { void getCar(); }
定义三个实现类:
public class SportCar implements Car { @Override public void getCar() { System.out.println("获得跑车"); } }
public class JeepCar implements Car { @Override public void getCar() { System.out.println("获得越野车"); } }
public class AudiCar implements Car { @Override public void getCar() { System.out.println("获得奥迪车"); } }
定义工厂:
public class CarFactory { public Car getCar(String carType) throws Exception { switch (carType){ case CarType.sportCarType: return new SportCar(); case CarType.jeepCarType: return new JeepCar(); case CarType.audiCarType: return new AudiCar(); default: throw new Exception("无车可用"); } } }
对应的CarType变量:
public class CarType { public static final String sportCarType = "0"; public static final String jeepCarType = "1"; public static final String audiCarType = "2"; }
测试一下:
public class Test { public static void main(String[] args){ try { CarFactory carFactory = new CarFactory(); Car car = carFactory.getCar(CarType.audiCarType); car.getCar(); } catch (Exception e) { e.printStackTrace(); } } }
结果:
获得奥迪车
总结一下简单工厂模式的优缺点:
- 优点:简单工厂模式能够根据外界给定的信息,决定究竟应该创建哪个具体类的对象。明确区分了各自的职责和权力,有利于整个软件体系结构的优化。
- 缺点:很明显工厂类集中了所有实例的创建逻辑,容易违反GRASPR的高内聚的责任分配原则
接下来看抽象工厂,参考《JAVA与模式》之抽象工厂模式
抽象工厂模式的优点
- 分离接口和实现
客户端使用抽象工厂来创建需要的对象,而客户端根本就不知道具体的实现是谁,客户端只是面向产品的接口编程而已。也就是说,客户端从具体的产品实现中解耦。
- 使切换产品族变得容易
因为一个具体的工厂实现代表的是一个产品族,比如上面例子的从Intel系列到AMD系列只需要切换一下具体工厂。
抽象工厂模式的缺点
- 不太容易扩展新的产品
如果需要给整个产品族添加一个新的产品,那么就需要修改抽象工厂,这样就会导致修改所有的工厂实现类。
最后总结一下简单工厂与抽象工厂的区别:
参考资料:详解设计模式六大原则