浅谈几种设计模式

一、简单工厂模式。

简单工厂模式一般是学习设计模式的基础。

举个简单的例子:一个客户,想要获取水果(苹果、菠萝、香蕉等),他会去告诉一个水果工厂他想要的水果种类(比如通过名字告诉),然后水果工厂根据名字,生成对应的水果,并返回。

我想要买一个菠萝,我告诉农场我想要的是菠萝,然后,农场给我生成一个菠萝。这就是简单工厂模式的一个例子。

这里有几个点:

1.菠萝:我想获得的产品,具体的产品类。

2.水果:菠萝属于的是水果的一种,这是一个抽象类,或者接口。

3.水果工厂:生成水果的工厂,是一个工厂类。

简单的工厂模式只需要包含上述3个点即可。我们只需要传入指定的参数,就可以获得需要的对象,同时不需要关注他是怎么被创建,也不需要我们手动通过new的方式创建。

来个例子理解一下(就是这么一个流程图):

用一个代码来加深印象:

场景:某公司需要开发一个图表库,包含有3种图表(HistogramChart、PieChart、LineChart)。根据设置不同的参数,需要得到不同的图表,同时还要有可扩展性。

我们来使用简单的工厂就可以。

(1)、Chart接口:代表抽象产品类。

public interface Chart {
    public void display();
}

(2)、HistogramChart:具体的产品类之一。

public class HistogramChart implements Chart{
    @Override
    public void display() {
        System.out.println("显示柱状图。");
    }

    public HistogramChart() {
        System.out.println("创建柱状图。");
    }
}

(3)、PieChart:具体的产品类之一。

public class PieChart implements Chart{
    @Override
    public void display() {
        System.out.println("显示饼状图。");
    }

    public PieChart() {
        System.out.println("创建饼状图");
    }
}

(4)、LineChart:具体的产品类之一。

public class LineChart implements Chart{
    @Override
    public void display() {
        System.out.println("显示折线图。");
    }

    public LineChart() {
        System.out.println("创建折线图。");
    }
}

(5)、工厂类。

//工厂类,根据name传递的值生成不同的类
public class ChartFactory {
    public static Chart getChart(String name){
        Chart chart = null;
        if (name.equalsIgnoreCase("histogram")){
            chart = new HistogramChart();
            System.out.println("初始化柱状图");
        } else if (name.equalsIgnoreCase("line")){
            chart = new LineChart();
            System.out.println("初始化折线图");
        } else if (name.equalsIgnoreCase("pie")){
            chart = new PieChart();
            System.out.println("初始化饼状图");
        }
        return chart;
    }
}

然后我们就可以测试一下:

public class Client {
    public static void main(String[] args) {
        //Chart chart = ChartFactory.getChart("line");//创建line图
        //chart.display();
        String chartType = XMLUtil.getChartType();
        Chart chart = ChartFactory.getChart(chartType);
        chart.display();
    }
}

注释掉的2行是直接通过传入名字的方式获取到的对象。我这里采用了另一种方式:从配置文件中读取信息。

建立XMLUtil工具类:

public class XMLUtil {
    //用于从配置文件中读取到图表类型,并且返回图表名
    public static String getChartType(){
        try {
            DocumentBuilderFactory dFactory = DocumentBuilderFactory.newInstance();
            DocumentBuilder builder = dFactory.newDocumentBuilder();
            Document doc;
            doc = builder.parse(new File("src//SimpleFactoryPattern//config.xml"));

            NodeList nl = doc.getElementsByTagName("chartType");
            Node classNode = nl.item(0).getFirstChild();
            String chartType = classNode.getNodeValue().trim();
            return chartType;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
}

在当前包下创建一个配置文件config.xml:

<?xml version="1.0"?>
<config>
    <chartType>histogram</chartType>
</config>

注意一下这里的路径一定要写对:

"src//SimpleFactoryPattern//config.xml"  src包下的SimpleFactoryPattern(包名)的config.xml

测试一下,运行Client的主方法: 

那么简单的工厂模式就是这样了。那么不难发现有一些缺点:

1.工厂类的任务过多,代码过于臃肿。我们在设计程序的时候,尽量要避免臃肿的程序,尽量的使代码分散,达到解耦的效果,这样也有利于后期的维护。

2.想要增加新的实现类,必须要到工厂类中修改代码,比较的麻烦。

那就可以用到下面新的设计模式了。

二、工厂方法模式

前面的简单工厂模式,一个工厂类就承担了所有职责,导致一个工厂负责创建全部的具体类,过于臃肿,工厂方法改进此窘况。

注意到了,在上面的简单工厂模式里,如果我要增加新的产品呢?

这样直接修改工厂类的做法违背了开闭原则。于是我们换一种思路完成: 

 增加了新的一层:具体按钮工厂。

把原来的工厂类抽象化,只定义通用的方法,让具体的工厂类去实现。这样在添加一个实现时,只需要在创建一个具体类来实现抽象工厂就可以了。

比如以下的场景:某系统运行行车记录仪(Logger)可以采取多种方式进行(FileLogger、DatabaseLogger),通过修改配置,可以轻松的切换记录仪的种类。

我们用工厂方法模式试试:

(1)、创建产品的抽象类(接口)Logger:

public interface Logger {//抽象的商品接口
    public void writeLog();
}

(2)、创建他的2个实现类FileLogger、DatabaseLogger:

public class FileLogger implements Logger{
    @Override
    public void writeLog() {
        System.out.println("文件日志记录");
    }
}
public class DatabaseLogger implements Logger {
    @Override
    public void writeLog() {
        System.out.println("数据库日志记录");
    }
}

(3)、设计一个日志接口,作为抽象工厂:

//抽象的工厂接口,定义一个通用的工厂方法
public interface LoggerFactory {
    public Logger creatLogger();//抽象的建立商品的方法,具体交给实现类来完成
}

(4)、设计具体的工厂类:

public class FileLoggerFactory implements LoggerFactory{
    @Override
    public Logger creatLogger() {
        Logger logger = new FileLogger();
        return logger;
    }
}

(5)、测试一下

public class Client {
    public static void main(String[] args) {
       LoggerFactory factory;
       Logger logger;
       factory = new FileLoggerFactory();
       logger = factory.creatLogger();
       logger.writeLog();
    }
}

采用此方法的话,通过new的方式,指定一个类型,构造出该类型的工厂类,在通过此工厂类来创建具体的产品。

换一种方法呢?

public class Client {
    public static void main(String[] args) {
//        LoggerFactory factory;
//        Logger logger;
//        factory = new FileLoggerFactory();
//        logger = factory.creatLogger();
//        logger.writeLog();
        LoggerFactory factory = (LoggerFactory) XMLUtil.getObjectBean();
        Logger logger = factory.creatLogger();
        logger.writeLog();
    }
}

这样我们就可以在配置文件中进行参数的传递了。同样的:

配置类:

<?xml version="1.0"?>
<config>
    <className>FactoryPattern.DatabaseLoggerFactory</className>
</config>

XMLUtil中声明新的方法,用于通过className获取对应的实例:

public static Object getObjectBean(){
        try {
            DocumentBuilderFactory dFactory = DocumentBuilderFactory.newInstance();
            DocumentBuilder builder = dFactory.newDocumentBuilder();
            Document doc;
            doc = builder.parse(new File("src//FactoryPattern//config.xml"));

            NodeList nl = doc.getElementsByTagName("className");
            Node classNode = nl.item(0).getFirstChild();
            String cName = classNode.getNodeValue();

            Class<?> c = Class.forName(cName);
            Object obj = c.newInstance();
            return obj;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

也要注意路径的写法别搞错了。

来试一试:

还真成了。

三、抽象工厂模式

抽象工厂模式,和上面的工厂方法模式,还是有一点相似的。体现在:角色方面。二者都是分成了:抽象工厂类,具体工厂类,抽象产品类,具体产品类。但是还是有差别的,我画个图:

有点凌乱。不过你可以这么理解:不同抽象产品的实现类之间是有一定联系的。举个例子:抽象产品A是空调,具体产品A1是美的空调,A2是格力空调;B是冰箱,B1是美的冰箱,B2是格力冰箱。工厂1是美的工厂,2是格力工厂。这么一来好理解了吧。

        来一个具体的场景呢?

 (1)、按钮接口,充当抽象产品类:

public interface Button {
    public void display();
}

按钮接口的2个具体实现类:

//具体的产品
public class SpringButton implements Button{
    @Override
    public void display() {
        System.out.println("显示浅绿色按钮。");
    }
}
public class SummerButton implements Button{
    @Override
    public void display() {
        System.out.println("显示浅蓝色按钮。");
    }
}

(2)、文本框接口,也是抽象产品类;

//文本框接口
public interface TextFiled {
    public void display();
}

具体实现:

public class SpringTextFiled implements TextFiled{
    @Override
    public void display() {
        System.out.println("显示浅绿色文本框。");
    }
}
public class SummerTextFiled implements TextFiled{
    @Override
    public void display() {
        System.out.println("显示浅蓝色文本框。");
    }
}

(3)、组合框接口:

//组合框接口,充当抽象产品
public interface ComboBox {
    public void display();
}

具体实现:

public class SpringComboBox implements ComboBox{
    @Override
    public void display() {
        System.out.println("显示浅绿色组合框。");
    }
}
public class SummerComboBox implements ComboBox{
    @Override
    public void display() {
        System.out.println("显示浅蓝色组合框。");
    }
}

(4)、抽象的工厂类:

//皮肤工厂接口,充当总的工厂接口
public interface SkinFactory {
    public Button createButton();
    public TextFiled createTextFiled();
    public ComboBox createComboBox();
}

(5)、具体的工厂:

public class SpringSkinFactory implements SkinFactory{
    @Override
    public Button createButton() {
        return new SpringButton();
    }

    @Override
    public TextFiled createTextFiled() {
        return new SpringTextFiled();
    }

    @Override
    public ComboBox createComboBox() {
        return new SpringComboBox();
    }
}
public class SummerSkinFactory implements SkinFactory{
    @Override
    public Button createButton() {
        return new SummerButton();
    }

    @Override
    public TextFiled createTextFiled() {
        return new SummerTextFiled();
    }

    @Override
    public ComboBox createComboBox() {
        return new SummerComboBox();
    }
}

写完了代码。来测试一下,比如我想得到一套夏天的皮肤:

那我配置文件就要这样写:

<?xml version="1.0"?>
<config>
    <className>AbstractFactoryPattern.SummerSkinFactory</className>
</config>

然后测一测:

public class Client {
    public static void main(String[] args) {
        SkinFactory factory;
        Button button;
        TextFiled textFiled;
        ComboBox comboBox;
        SkinFactory skinFactory = (SkinFactory)XMLUtil.getObjectBean();
        button = skinFactory.createButton();
        textFiled = skinFactory.createTextFiled();
        comboBox = skinFactory.createComboBox();
        button.display();
        textFiled.display();
        comboBox.display();
    }
}

结果:

四、单例模式

前面几种工厂模式介绍完了,现在到单例模式。单例模式也是一个应用非常广泛的模式了,spring中的bean默认就是单例的。

单例模式的实现可以分成有:饿汉式、懒汉式。

饿汉式:只要类一进行加载,这个唯一的实例就会被创建。代码实现:

public class EagerSingleton {
    private static final EagerSingleton instance = new EagerSingleton();//直接在生成类的时候创建好

    public EagerSingleton() {
    }

    public static EagerSingleton getInstance(){
        return instance;
    }
}

注意这个instance单例,要用到static修饰,代表着是随着类而加载的;还要有个final,表示这个单例不可以再被修改。

看看到底是不是单例:

public class Test {
    public static void main(String[] args) {
        EagerSingleton i1 = EagerSingleton.getInstance();
        EagerSingleton i2 = EagerSingleton.getInstance();
        System.out.println(i1 == i2);

//        LazySingleton l1 = LazySingleton.getInstance();
//        LazySingleton l2 = LazySingleton.getInstance();
//        System.out.println(l1 == l2);
    }
}

结果:

 发现了两次调用获取实例方法,获得的对象是同一个(指向了同一个地址)。

懒汉式:什么意思呢?这个类比较懒,只有当你需要获取实例的时候,才进行创建:

public class LazySingleton {
    private static LazySingleton instance = null;

    public LazySingleton() {
    }

    public static LazySingleton getInstance(){
        if (instance == null){
            synchronized (LazySingleton.class){
                instance = new LazySingleton();//懒汉式:在调用方法时才进行创建,注意要用同步代码块包装起来。
            }
        }
        return instance;
    }
}

这里面用同步代码块包裹起来,是怕同时有两个线程调用getInstance的时候,会出现创建了2个实例,不信我们模拟一下:

public class ThreadSingleInstanceTest {
    public static void main(String[] args) {
        ThreadA threadA = new ThreadA();
        ThreadB threadB = new ThreadB();
        threadA.start();
        threadB.start();
    }

    static class ThreadA extends Thread{
        @Override
        public void run() {
            while (true){
                LazySingleton i1 = LazySingleton.getInstance();
                System.out.println(currentThread().getName() + i1);
                try {
                    sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    static class ThreadB extends Thread{
        @Override
        public void run() {
            while (true){
                LazySingleton i2 = LazySingleton.getInstance();
                System.out.println(currentThread().getName() + i2);
                try {
                    sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

        }
    }
}

首先看看加了同步的情况:

 输出的地址都是一样的,单例无疑了。

我把synchronized去掉呢?

不对劲了,所以要加上同步代码块,保证唯一性。

如果某一瞬间,线程A,B都通过了instance == null的判断,然后A先拿到锁,创建了一个实例;B后拿到锁,也创建另一个实例。这样单例模式就被破坏。这时候我们就可以采用双层检查锁定的模式创建单例:

public class DoubleCheckLockingSingleton {
    //双层检查锁实现的懒汉单例模式,绝对安全。
    private static volatile DoubleCheckLockingSingleton instance = null;
    //volatile保证多线程正确处理
    public static DoubleCheckLockingSingleton getInstance(){
        if (instance == null){//第一层检查
            synchronized (DoubleCheckLockingSingleton.class){
                if (instance == null){//第二层判断
                    instance = new DoubleCheckLockingSingleton();
                }
            }
        }
        return instance;
    }
}

 还可以通过静态内部类的方式创建一个单例:

public class InnerClassSingleton {
    //静态内部类,只会随着类加载执行一次。保证单例
    private static class Inner{
        private static final InnerClassSingleton instance = new InnerClassSingleton();
    }
    public static InnerClassSingleton getInstance(){
        return Inner.instance;//返回静态内部类中的实例。
    }
}

这样也是比较安全的,而且代码还比较简洁。

总结:单例模式适用于:只需要一个对象,或者只允许存在一个对象的情况。

五、建造者模式

我来画一张流程图:

 这里有4个部分组成,解释一下:

Builder:抽象建造者。这里面只需要定义创建一个个零件的方法即可,注意还需要定义一个方法,用来返回创建结果Result。

ConcreteBuilder:具体的建造者。对Builder的具体实现。他具体实现了如何创建一个个零件的方法,并且返回创建的具体结果。

Product:被Builder创建出来的产品。

Dirctor:导演类。负责安排复杂对象的构建顺序,一般用户与Dirctor沟通即可获得构建结果。

比如以下的场景:我想构建不同的角色,比如天使,魔鬼,战神,每个不同角色的构建步骤大同小异。并且要在未来便于我新增新的角色的构建。这样的话我们就可以用建造者模式来实现。

 (1)、Actor复杂对象类的编写:

//Actor类,充当复杂对象,待建造,需要建造者建造。
public class Actor {
    private String type;
    private String sex;
    private String face;
    private String costume;
    private String hairstyle;

    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public String getFace() {
        return face;
    }

    public void setFace(String face) {
        this.face = face;
    }

    public String getCostume() {
        return costume;
    }

    public void setCostume(String costume) {
        this.costume = costume;
    }

    public String getHairstyle() {
        return hairstyle;
    }

    public void setHairstyle(String hairstyle) {
        this.hairstyle = hairstyle;
    }
}

(2)、抽象建造者:

//抽象类ActorBuilder,充当抽象建造者,具体功能还需要实现类来完成
public abstract class ActorBuilder {
    protected Actor actor = new Actor();

    public abstract void buildType();
    public abstract void buildSex();
    public abstract void buildFace();
    public abstract void buildCostume();
    public abstract void buildHairstyle();

    //工厂方法,返回一个完整的对象
    public Actor creatActor(){
        return actor;
    }
}

(3)、具体的建造者:

//天使创建者,充当了具体建造者
public class AngleBuilder extends ActorBuilder{

    @Override
    public void buildType() {
        actor.setType("天使");
    }

    @Override
    public void buildSex() {
        actor.setSex("女");
    }

    @Override
    public void buildFace() {
        actor.setFace("美丽");
    }

    @Override
    public void buildCostume() {
        actor.setCostume("长裙");
    }

    @Override
    public void buildHairstyle() {
        actor.setHairstyle("蓬松");
    }
}
//恶魔创建者,充当了具体建造者
public class DevilBuilder extends ActorBuilder{

    @Override
    public void buildType() {
        actor.setType("魔鬼");
    }

    @Override
    public void buildSex() {
        actor.setSex("男");
    }

    @Override
    public void buildFace() {
        actor.setFace("丑陋");
    }

    @Override
    public void buildCostume() {
        actor.setCostume("法袍");
    }

    @Override
    public void buildHairstyle() {
        actor.setHairstyle("光头");
    }
}
//英雄创建者,充当了具体建造者
public class HeroBuilder extends ActorBuilder{

    @Override
    public void buildType() {
        actor.setType("英雄");
    }

    @Override
    public void buildSex() {
        actor.setSex("男");
    }

    @Override
    public void buildFace() {
        actor.setFace("英俊");
    }

    @Override
    public void buildCostume() {
        actor.setCostume("赤膊");
    }

    @Override
    public void buildHairstyle() {
        actor.setHairstyle("茂密");
    }
}

(4)、角色控制器,充当指挥者

//角色控制器,充当指挥者
public class ActorController {
    //构建复杂产品对象
    public Actor construct(ActorBuilder ab){
        Actor actor;
        ab.buildType();
        ab.buildFace();
        ab.buildCostume();
        ab.buildHairstyle();
        ab.buildSex();
        actor = ab.creatActor();
        return actor;
    }
}

(5)、配置文件。我想构建天使角色

<?xml version="1.0"?>
<config>
    <className>BuilderPattern.AngleBuilder</className>
</config>

(6)、来测试看看:

public class Client {
    public static void main(String[] args) {
        ActorBuilder ab;//创建一个建造者
        ab = (ActorBuilder)XMLUtil.getObjectBean();

        ActorController actorController = new ActorController();
        Actor actor = actorController.construct(ab);
        System.out.println(actor.getSex());
        System.out.println(actor.getCostume());
        System.out.println(actor.getFace());
        System.out.println(actor.getHairstyle());
        System.out.println(actor.getType());

    }
}

结果:

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值