第一章 设计模式介绍及简单工厂模式

希望这是一个好习惯的开始。

如何设计出优雅、简洁、可扩展的编程风格,是每个程序员都需要认真对待的事情,借助常用的设计模式的综合使用及取舍,可帮助我们在编写代码时,深入理解面向对象的设计思想,使用设计模式的机制,设计出简单易复用的模式和结构,从而提高开发效率,节约开发成本。coding不止是一门技术,也是一门艺术。

设计模式原则

    使用设计模式时,我们常遵循5大原则,当然有的地方也说是7大原则。

  • 开闭原则:开闭原则主要是对扩展开放,对修改关闭。主要思想是尽量扩展原有功能,有新需求时不应修改原有代码的逻辑,不要有强侵入性。例如:1、可以使用接口和实现分离的方式,可以把不变的功能抽象成一个接口,这些不变的接口可以应对未来的扩展,未来需求的部分可以扩展新的接口来实现。2、模块之间调用通过使用接口,即使接口实现的逻辑改变,调用方也不必改变调用方式;同时接口的实现还可以做到柔性扩展。开闭原则也是以下几种设计原则的基石,我们也常把开闭原则等同于面向接口的软件设计。
  • 依赖倒置原则:在coding时,我们常使用的是提供接口对外进行服务,实现类实现接口具象化相应功能。这里的依赖倒置原则就是要依赖抽象来编程,不要依赖具体来编程,要求调用方要依赖抽象进行耦合。意思就是客户端要调用接口来获取信息,不要调用实际的类。依赖倒置原则基于这样一个事实:相对于细节的多变性,抽象的东西要稳定的多。以抽象为基础搭建起来的架构比以细节为基础搭建起来的架构要稳定的多。在java中,抽象指的是接口或者抽象类,细节就是具体的实现类,使用接口或者抽象类的目的是制定好规范和契约,而不去涉及任何具体的操作,把展现细节的任务交给他们的实现类去完成。核心思想还是面向接口编程。
  • 单一职责原则:单一职责原则相对好理解一些,就是一个类/接口/方法只负责一个职责,就是尽量只做一个功能,不要把好多功能都扩展在一个类/接口/方法内,这样显得很臃肿,后期也比较难以扩展和维护。当然这个原则也不是绝对的,如果类/接口/方法的职责细化过于细致,也是较为臃肿的,需要把相类似的功能放在一块,是一个平衡的过程。
  • 接口隔离原则:接口隔离原则主要就是,客户端不应该依赖他不需要的接口,就是一个类实现接口中的方法都是类需要的,不需要的方法不应该放在这个接口中,应该创建另一个接口进行分离。看到这里,有人会说单一职责和接口隔离看着很类似啊,其实不然:1、单一职责主要注重的是职责,接口隔离原则主要注重对接口依赖的隔离。2、单一职责原则主要约束的是类,其次是接口和方法,针对的是程序中的实现和细节;而接口隔离原则主要是约束接口,是针对整体框架的构建。
  • 里氏替换原则:所有引用基类的地方必须能透明地使用其子类的对象。类似就是“父子”关系,就是父亲会的,儿子都会,儿子还会扩展。很多开源框架的接口,都是通过继承父类而来的。
  • 迪米特法则:也是最少知道原则。可以简单说成 talk only to your immediate friends。也是我们程序设计的目的,高内聚,低耦合。应该把最少得东西对外暴露,每个类需要包装好自己的访问权限,强调的是类之间的松耦合,耦合越弱,越有利于复用。

 

简单工厂模式

简单工厂模式不属于23种设计模式,不是工厂模式,它是一种编码风格和习惯,有利于理解工厂模式中的工厂方法模式和抽象工厂模式,我们来看一下简单工厂模式。

下面我们以生产电脑为例,进行说明:

抽象电脑类:

/**
 * @program: adpn-pattern->Computer
 * @description: 电脑类
 * @author: Jstar
 * @create: 2019-11-17 11:18
 **/
public abstract class Computer {
    public abstract void process();
}

实现类(联想电脑):

/**
 * @program: adpn-pattern->ThinkpadComputer
 * @description: ThinkPad电脑
 * @author: Jstar
 * @create: 2019-11-17 11:26
 **/
public class ThinkpadComputer extends Computer{
    @Override
    public void process() {
        System.out.println("这里可写业务代码,但我们模拟--生产联想电脑");
    }
}

实现类(苹果电脑):

/**
 * @program: adpn-pattern->MacbookComputer
 * @description: 苹果电脑
 * @author: Jstar
 * @create: 2019-11-17 11:31
 **/
public class MacbookComputer extends Computer {

    @Override
    public void process() {
        System.out.println("这里可写业务代码,但我们模拟--生产苹果电脑");
    }
}

生产电脑的简单工厂方法:

/**
 * @program: adpn-pattern->ComputerFactory
 * @description: 生产电脑工厂类
 * @author: Jstar
 * @create: 2019-11-17 11:34
 **/
public class ComputerFactory {
    public Computer getComputer(String type){
        if("thinkpad".equalsIgnoreCase(type)){
            return new ThinkpadComputer();
        }else if("macbook".equalsIgnoreCase(type)){
            return new MacbookComputer();
        }else{
            return null;
        }
    }
}

好了,简单工厂的模式我们已经完成了,下面开始模拟客户端进行调用:

/**
 * @program: adpn-pattern->Test
 * @description: 测试类
 * @author: Jstar
 * @create: 2019-11-17 11:42
 **/
public class Test {
    public static void main(String[] args) {
        ComputerFactory computerFactory = new ComputerFactory();
        Computer computer = computerFactory.getComputer("macbook");
        if(computer!=null){
            computer.process();
        }
    }
}

这是代码的UML 图:

代码执行了,这是我们的结果返回:

这里可写业务代码,但我们模拟--生产苹果电脑

至此,简单工厂模式我们已经完成了,主要根据我们传入生产电脑的类型,简单工厂就会给我们返回 想要生产的电脑。

但是你想没想到一个问题,假如...我们想生产一个惠普电脑,那...我们就得在工厂类 ComputerFactory 的getComputer 方法中添加获取 惠普电脑的判断和返回。这.....不就违反我们开篇写的 开闭原则了嘛。

嗯。。那我们继续改:

下面我们使用反射的方式,把工厂类ComputerFactory 改一下:

/**
 * @program: adpn-pattern->ComputerFactory
 * @description: 生产电脑工厂类
 * @author: gumaomao_Jstar
 * @create: 2019-11-17 11:34
 **/
public class ComputerFactory {

    public Computer getComputer(Class c){
        Computer computer=null;
        try {
            computer = (Computer) Class.forName(c.getName()).newInstance();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return computer;
    }
}

我们再加个生产惠普电脑的类:

/**
 * @program: adpn-pattern->HPComputer
 * @description: 惠普电脑
 * @author: Jstar
 * @create: 2019-11-17 12:11
 **/
public class HPComputer extends Computer{

    @Override
    public void process() {
        System.out.println("这里可写业务代码,但我们模拟--生产惠普电脑");
    }
}

客户端测试类,我们只改一行代码即可:

/**
 * @program: adpn-pattern->Test
 * @description: 测试类
 * @author: Jstar
 * @create: 2019-11-17 11:42
 **/
public class Test {
    public static void main(String[] args) {
        ComputerFactory computerFactory = new ComputerFactory();
        //Computer computer = computerFactory.getComputer("macbook");
        Computer computer = computerFactory.getComputer(HPComputer.class);
        if(computer!=null){
            computer.process();
        }
    }
}
更改之后,我们来看一下项目的UML图:

现在已经不是强依赖的关系,后期扩展就比较方便了。

下面我们再测试一下:

这里可写业务代码,但我们模拟--生产惠普电脑

ok,这样我们就遵循了开闭原则,以后再添加生产新类型的电脑,我们只需要添加 新电脑和更改客户端调用即可。

至此,非设计模式的简单工厂模式我们就讲完了。

下面,我们再来扩展一下,简单工厂模式在jdk中的使用

1、简单工厂模式--Calendar 类,根据传入不同参数类型,创建不同的实例 如:

/**
 * @program: adpn-pattern->JdkCalenderLearn
 * @description: 看Calender 中使用简单工厂模式
 * @author: Jstar
 * @create: 2019-11-17 16:06
 **/
public class JdkCalenderLearn {
    public static void main(String[] args) {
        // 其日历字段已由当前日期和时间初始化:
        Calendar rightNow = Calendar.getInstance(); // 子类对象
        // 获取年
        int year = rightNow.get(Calendar.YEAR);
        // 获取月
        int month = rightNow.get(Calendar.MONTH);
        // 获取日
        int date = rightNow.get(Calendar.DATE);
        //获取几点
        int hour=rightNow.get(Calendar.HOUR_OF_DAY);
        System.out.println(year + "年" + (month + 1) + "月" + date + "日"+hour+"时");
    }
}

我们来看看 Calendar.getInstance() 都做了什么:

public static Calendar getInstance()
    {
        return createCalendar(TimeZone.getDefault(), Locale.getDefault(Locale.Category.FORMAT));
    }

我们来看 rerun 后的具体实现:

图上红框的地方就是使用了,我们上面讲的第一种简单工厂模式,根据不同的类型,返回不同的实例。

我们来看一下 Calender 的UML类图:

2、 简单工厂模式--JDBC 使用反射来注册驱动,获取连接:

String className = "com.mysql.jdbc.Driver";//mysql完整的包名+类名

//String className = "oracle.jdbc.driver.OracleDriver";//oracle全路径名

Driver driver = (Driver)Class.forName(driverClass).newInstance();//通过反射实例化这个类

好了,本次分享就先到这吧。

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值