设计模式学习记录
于本人而言,设计模式这方面的知识点之前看视频匆匆过了一篇,项目上部分设计模式没有涉及,有些知识点就时常忘记,仅以此篇重新学习设计模式,只记录个人学习过程,理解错误之处请看到此篇者多多指教。由此感谢。
一 、工厂模式
1. 简单工厂模式
理解:简单工厂类负责创建的对象较少的场景,且客户端只需要传入工厂类的参数,对于如何创建对象的逻辑不需要关心。现在有这样一种情景:有三个品牌的手机,华为,苹果,小米需要在工厂中创建,通过配置简单工厂完成。
手机类图:![在这里插入图片描述](https://img-blog.csdnimg.cn/20201209225628461.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM4MjcwMTk2,size_16,color_FFFFFF,t_70)
手机类图代码:为了记录简洁,对象与接口代码复制在一起,试验的时还请分开,否则无法编译
public interface Phone {
void call();
}
public class IphoneBrand implements Phone{
public void call() {
System.out.println("这里是-- 苹果手机-- 在呼叫! ");
}
}
public class HuawaiBrand implements Phone {
public void call() {
System.out.println("这里是-- 华为手机-- 在呼叫! ");
}
}
public class XiaomiBrand implements Phone{
public void call() {
System.out.println("这里是-- 小米手机-- 在呼叫! ");
}
}
简单工厂类图:
简单工厂代码版本一:
public class PhoneFactoryV1 {
public Phone create(String phoneBrand) {
if ("huawai".equals(phoneBrand)) {
return new HuawaiBrand();
} else if ("iphone".equals(phoneBrand)) {
return new IphoneBrand();
} else if ("xiaomi".equals(phoneBrand)) {
return new XiaomiBrand();
} else {
return null;
}
}
}
客户端代码:
public class Client {
public static void main(String[] args) {
PhoneFactoryV1 factoryV1 = new PhoneFactoryV1();
Phone phone = factoryV1.create("iphone");
phone.call();
}
}
心得:客户端直接new这个简单工厂类,传入参数创建对应的手机类就OK了。这种代码不方便处很显然,因是根据所传入的手机品牌名称进行创建,所以在以后一旦增加手机品牌,就必须要去更改工厂类的代码,也就是说每加入一个品牌都需要加入一个if判断条件,这显然是不合理的,也违背了设计原则。
简单工厂代码版本二:
public class PhoneFactoryV2 {
public Phone create(String className) {
try {
if (!(null == className) || "".equals(className)) {
return (Phone)Class.forName(className).newInstance();
}
}catch (Exception e){
e.printStackTrace();
}
return null;
}
}
客户端代码:
public class Client {
public static void main(String[] args) {
PhoneFactoryV2 factoryV2 = new PhoneFactoryV2();
Phone phone = factoryV2.create("com.stu.factory.simplefactory.IphoneBrand");
phone.call();
}
}
心得:通过反射机制可以实现对象建立,不需要在工厂类中多次判断创建哪个对象的业务逻辑,但是这种方式传入类的路径,传入这种字符串比较麻烦,所以可以进一步改善。
简单工厂代码版本三:
public class PhoneFactoryV3 {
public Phone create(Class clazz) {
try {
if (null != clazz) {
return (Phone) clazz.newInstance();
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
客户端代码:
public class Client {
public static void main(String[] args) {
PhoneFactoryV3 factoryV3 = new PhoneFactoryV3();
Phone phone = factoryV3.create(HuawaiBrand.class);
phone.call();
}
}
心得:这种写法既不用在工厂类中写多个判断,也不用传入相对较长的类名,个人觉得比较好的写法。
2. 工厂方法模式
理解:定义一个创建对象的接口,但让实现这个接口的类决定实例化哪个类,工厂方法让类的实例话推迟到子类中进行。优点:用户只需要关心所需产品对应的工厂,无需关注创建细节,加入新产品符合开闭原则,提高了系统的可扩展性。缺点:类的个数容易过多,增加了代码接口的复杂度。
工厂方法类图:
工厂方法代码版本一(内含多类):
public interface FactoryMethodV1 {
Phone create();
}
public class HuawaiFactory implements FactoryMethodV1{
@Override
public Phone create() {
return new HuawaiBrand();
}
}
public class IphoneFactory implements FactoryMethodV1{
@Override
public Phone create() {
return new IphoneBrand();
}
}
public class XiaomiFactory implements FactoryMethodV1{
@Override
public Phone create() {
return new XiaomiBrand();
}
}
客户端代码:
public class Client {
public static void main(String[] args) {
FactoryMethodV1 huawaiFactory = new HuawaiFactory();
Phone phone = huawaiFactory.create();
phone.call();
}
}
心得:就比如手机工厂越来越大,一个工厂已经不满足所有品牌的生产了,所以品牌独立门户,每一个品牌都有自己的工厂,只注重自身的产品就可以。但是还需要实现总工厂的接口,通过总工厂定义,约束创建方法,然后由子工厂实现。
3. 抽象工厂模式
理解:这种模式思维我觉得跟工厂方法的思维很像,只是可以在总的工厂方法中增加多个方法接口。之前也在百度抽象工厂与工厂方法的区别,我觉得最大的区别就是工厂方法的工厂类只有建造方法,而抽象工厂可以有多个方法,也就是说可以增加多个接口让子类去实现。
就好比现在手机行业要区分版本,分高配,常规,低配版本的手机,这样就增加了一个维度,在产品的角度上来说,手机分配置和品牌两个维度。所以需要在抽象工厂中创建高配,常规,低配的手机接口让子类实现。这样就实现了两个维度上的区分。
高配手机类图(该类图与顶部手机类图同理 略):
高配手机代码(内含多类):
public interface HighPhone {
void call();
}
public class HighHuawaiBrand implements HighPhone{
@Override
public void call() {
System.out.println("高配版本 -- 华为 --- 呼叫!");
}
}
public class HighIphoneBrand implements HighPhone{
@Override
public void call() {
System.out.println("高配版本 -- 苹果 --- 呼叫!");
}
}
public class HighXiaomiBrand implements HighPhone{
@Override
public void call() {
System.out.println("高配版本 -- 小米 --- 呼叫!");
}
}
抽象工厂类图:
抽象工厂代码 版本一:
//抽象工厂类可以定义多个接口供子类实现,创建低配,常规,高配手机接口(低配同理其他两个接口,代码省略)
public interface IphoneAbastractFactory {
Phone create();
HighPhone createHigh();
//void createLow();
}
//子类(品牌工厂类)实现父类接口,创建自己品牌对应低中高版本的手机即可。这样就区分开品牌和版本的两个维度了。
public class IphoneFactory implements IphoneAbastractFactory{
@Override
public Phone create() {
return new IphoneBrand();
}
@Override
public HighPhone createHigh() {
return new HighIphoneBrand();
}
}
public class XiaomiFactory implements IphoneAbastractFactory{
@Override
public Phone create() {
return new XiaomiBrand();
}
@Override
public HighPhone createHigh() {
return new HighXiaomiBrand();
}
}
public class HuawaiFactory implements IphoneAbastractFactory {
@Override
public Phone create() {
return new HuawaiBrand();
}
@Override
public HighPhone createHigh() {
return new HighHuawaiBrand();
}
}
二 、单例模式
1. 饿汉式单例模式
代码示例一:
public class HungrySingleton {
private static final HungrySingleton hungrySingleton = new HungrySingleton();
private HungrySingleton(){};
public static HungrySingleton getInstance(){
return hungrySingleton;
}
}
代码示例二:
//缺点:不管用没用,都初始化了, 浪费内存空间
public class HungryStaticSingleton {
private static final HungryStaticSingleton hungryStaticSingleton;
static {
hungryStaticSingleton=new HungryStaticSingleton();
}
private HungryStaticSingleton(){};
public static HungryStaticSingleton getInstance(){
return hungryStaticSingleton;
}
}
2. 懒汉式单例模式
理解:懒汉模式线程不安全
public class LazySimpleSingleton {
private static LazySimpleSingleton lazy = null;
private LazySimpleSingleton() {
}
public static LazySimpleSingleton getInstance() {
//线程不安全
if (lazy==null){
lazy = new LazySimpleSingleton();
}
return lazy;
}
}
3. 双重锁单例模式
理解:改善懒汉式的线程不安全,引出双重锁写法
public class LazyDoubleCheckSingleton {
private volatile static LazyDoubleCheckSingleton lazy = null;
private LazyDoubleCheckSingleton() {
//防止反射注入
if (LazyDoubleCheckSingleton.lazy == null){
throw new RuntimeException("不允许构建多个实例!");
}
}
public static LazyDoubleCheckSingleton getInstance() {
//线程不安全
if (lazy==null){
/* 第二个if(lazy == null) 的意义:
线程0,线程1 同时进入当前位置(同时进入第一个if),线程0会锁住当前方法,线程1会进入monitor模式,当线程0执行 synchronized锁住的方法后,释放锁。
此时线程1进入Running模式,会再次进行new 实例化的操作,所以还需要存在第二个if,判断实例是否存在。双重锁。*/
synchronized(LazySimpleSingleton.class) {
if (lazy == null) {
lazy = new LazyDoubleCheckSingleton();
}
}
}
return lazy;
}
}
4. 内部类的单例模式
理解:个人认为是比较优的一种写法。
//内部类的懒加载模式
//性能最优的一种写法
public class LazyInnerClassSingleton {
//构造方法私有化,但是可以被反射攻击
private LazyInnerClassSingleton(){
if (LazyHolder.LAZY!=null){
throw new RuntimeException("不允许构建多个实例!");
}
}
public static final LazyInnerClassSingleton getInstance(){
return LazyHolder.LAZY;
}
//懒汉式单例
//LazyHolder里边的逻辑需要等到外部方法调用时才执行
//巧妙利用了内部类的特性
//JVM底层执行逻辑,完美避免了线程安全的问题
private static class LazyHolder{
private static final LazyInnerClassSingleton LAZY=new LazyInnerClassSingleton();
}
}
4. 枚举的单例模式
public enum EnumSingleton {
INSTANCE;
private Object data;
public Object getData() {
return data;
}
public void setData() {
this.data = data;
}
public void setData(Object data) {
this.data = data;
}
public static EnumSingleton getInstance() {
return INSTANCE;
}
}
三、原型模式
理解:原型模式的意义是拷贝对象,省略创建多个对象的代码。
1. 浅克隆模式
理解:浅克隆的模式,引用类型在copy的时候,只copy了地址值,并没有把真正的值copy到克隆对象中,所以更改一个对象的引用类型的值的时候,所有的克隆对象的引用值都会被改变,所以不推荐这种方式。
浅克隆类图:
心得:个人认为Client的作用就是找到具体Prototype子类的克隆方法,把克隆对象传进来,调用克隆对象的克隆方法。
浅克隆代码示例一:
客户端代码
public class Client {
private Prototype prototype;
public Client(Prototype prototype){
this.prototype = prototype;
}
public Prototype startClone(Prototype concretePrototype){
return (Prototype)concretePrototype.clone();
}
}
原型接口 + 原型类代码
public interface Prototype{
Prototype clone();
}
public class ConcretePrototypeA implements Prototype {
private int age;
private String name;
private List hobbies;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List getHobbies() {
return hobbies;
}
public void setHobbies(List hobbies) {
this.hobbies = hobbies;
}
@Override
public ConcretePrototypeA clone() {
ConcretePrototypeA concretePrototype = new ConcretePrototypeA();
concretePrototype.setAge(this.age);
concretePrototype.setName(this.name);
concretePrototype.setHobbies(this.hobbies);
return concretePrototype;
}
}
public class ConcretePrototypeB implements Prototype {
@Override
public Prototype clone() {
return null;
}
}
测试类代码
public class PrototypeTest {
public static void main(String[] args) {
// 创建一个具体的需要克隆的对象
ConcretePrototypeA concretePrototype = new ConcretePrototypeA();
// 填充属性,方便测试
concretePrototype.setAge(18);
concretePrototype.setName("prototype");
List hobbies = new ArrayList<String>();
concretePrototype.setHobbies(hobbies);
System.out.println(concretePrototype);
// 创建Client对象,准备开始克隆
Client client = new Client(concretePrototype);
ConcretePrototypeA concretePrototypeClone = (ConcretePrototypeA) client.startClone(concretePrototype);
System.out.println(concretePrototypeClone);
System.out.println("克隆对象中的引用类型地址值:" + concretePrototypeClone.getHobbies());
System.out.println("原对象中的引用类型地址值:" + concretePrototype.getHobbies());
System.out.println("对象地址比较:"+(concretePrototypeClone.getHobbies() == concretePrototype.getHobbies()));
}
}
2. 深克隆模式
理解:深克隆模式会克隆出两个实例,利用序列化的方式
原型接口 + 原型类代码
public class JinGuBang implements Serializable {
public float h = 100;
public float d = 10;
public void big(){
this.d *= 2;
this.h *= 2;
}
public void small(){
this.d /= 2;
this.h /= 2;
}
}
public class Monkey {
public int height;
public int weight;
public Date birthday;
}
public class QiTianDaSheng extends Monkey implements Cloneable,Serializable {
public JinGuBang jinGuBang;
public QiTianDaSheng(){
//只是初始化
this.birthday = new Date();
this.jinGuBang = new JinGuBang();
}
@Override
protected Object clone() throws CloneNotSupportedException {
return this.deepClone();
}
public Object deepClone(){
try{
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(this);
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
QiTianDaSheng copy = (QiTianDaSheng)ois.readObject();
copy.birthday = new Date();
return copy;
}catch (Exception e){
e.printStackTrace();
return null;
}
}
public QiTianDaSheng shallowClone(QiTianDaSheng target){
QiTianDaSheng qiTianDaSheng = new QiTianDaSheng();
qiTianDaSheng.height = target.height;
qiTianDaSheng.weight = target.height;
qiTianDaSheng.jinGuBang = target.jinGuBang;
qiTianDaSheng.birthday = new Date();
return qiTianDaSheng;
}
}
测试类代码
public class DeepCloneTest {
public static void main(String[] args) {
QiTianDaSheng qiTianDaSheng = new QiTianDaSheng();
try {
QiTianDaSheng clone = (QiTianDaSheng)qiTianDaSheng.clone();
System.out.println("深克隆:" + (qiTianDaSheng.jinGuBang == clone.jinGuBang));
System.out.println(qiTianDaSheng.birthday+":"+clone.birthday);
} catch (Exception e) {
e.printStackTrace();
}
QiTianDaSheng q = new QiTianDaSheng();
QiTianDaSheng n = q.shallowClone(q);
System.out.println("浅克隆:" + (q.jinGuBang == n.jinGuBang));
}
}
三、代理模式
1. jdk实现代理模式
public class JDKMeipo implements InvocationHandler {
private Object target;
public Object getInstance(Object target){
this.target=target;
Class<?> clazz= target.getClass();
return Proxy.newProxyInstance(clazz.getClassLoader(),clazz.getInterfaces(),this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("代理类操作==================前");
Object obj = method.invoke(this.target, args);
System.out.println("代理类操作==================后");
return obj;
}
}
public interface Person {
void findLove();
}
public class Girl implements Person{
@Override
public void findLove() {
System.out.println("女孩儿想寻找高富帅!");
}
}
public class Test {
public static void main(String[] args) {
try {
Object girl = new JDKMeipo().getInstance(new Girl());
Method findLove = girl.getClass().getMethod("findLove", null);
findLove.invoke(girl);
} catch (Exception e) {
e.printStackTrace();
}
}
}
2. cglib实现代理模式
public class CGlibMeipo implements MethodInterceptor {
public Object getInstance(Class<?> clazz) throws Exception{
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(clazz);
enhancer.setCallback(this);
return enhancer.create();
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("代理类------------------------操作前");
Object obj = methodProxy.invokeSuper(o, objects);
System.out.println("代理类------------------------操作后");
return obj;
}
}
public class Customer {
public void findLove(){
System.out.println("儿子要求:肤白貌美大长腿");
}
}
public class Test {
public static void main(String[] args) throws Exception {
Customer obj = (Customer) new CGlibMeipo().getInstance(Customer.class);
obj.findLove();
}
}