虫虫的技术博客 技术 生活

Tuesday, May 21, 2019

设计模式-建造者模式

        建造者模式,又叫构建者模式,构建器模式等等,定义这里我就不赘述了,网上书上都很多,可以很方便找来看。我为什么不说一遍呢,第一,觉得大部分的定义都描述的不是很易懂,第二,我自己目前还没达到能够下定义的水平。
        先来看看代码,然后再给出我的理解。

public class Article {
    private String title;
    private String content;
    private String sign;
    private Date time;
//省略了getter,setter,toString

}

public interface ArticleBuilder {
    void setTitle(String title);
    void buildContent(String title);
    void setTime(Date date);
    Article getResultArticle();
}

public class ConcreteArticleBuilder implements ArticleBuilder {
    private Article article = new Article();
    @Override
    public void setTitle(String title) {
        article.setTitle(title);
    }

    @Override
    public void buildContent(String content) {
        article.setContent(content);
    }

    @Override
    public void setTime(Date date) {
        article.setTime(date);
    }

    @Override
    public Article getResultArticle() {
        return article;
    }
}

public class ArticleWriter {

    private ArticleBuilder builder;
    public ArticleWriter(ArticleBuilder builder) {
        this.builder = builder;
    }

    public Article construct() {
        builder.setTitle("");
        builder.buildContent("");
        builder.setTime(new Date());
        return builder.getResultArticle();
    }
}

public class BuilderPatternDemo {
    public static void main(String[] args) {
        ArticleBuilder carBuilder = new ConcreteArticleBuilder();
        ArticleWriter cd = new ArticleWriter(carBuilder);
        Article car = cd.construct();
        System.out.println(car);
    }
}



建造者模式的核心类是Builder接口和具体的Builder实现类,比如StringBuilder,我们在使用的过程中,几乎忽略了Director角色和抽象Builder(Appendable)的存在,而Director角色也通常是业务逻辑中的角色,对于抽象程度不高的业务代码中,没有什么影响。对于一个架构良好的设计中,各角色的存在还是很有必要的。

建造者模式和工厂模式的区别在于,工厂模式使用类似于流水线的方式目的在于生产一批对象,而建造者模式核心的关注点是如何创建一个对象,以及创建过程中的每个细节。工厂模式的适用于批量创建多个对象的场景,建造者模式适用创建单个复杂的不同对象,用建造者创建的多个对象之间的差异较大。
再用另外一个比喻,一个是工厂,生产出来的是千篇一律的产品,另外一个是指挥家,创造的音乐是有个性的,几乎是唯一的音乐。
再比如,创作一幅画,这个也可以用建造者模式

建造者模式在JDK中身影:StringBuilder

下面再附上实现《设计模式》中的Builder模式的代码:

Sunday, May 12, 2019

设计模式-抽象工厂

        工厂方法模式中,工厂类用于生成一系列对象实例并且以这些对象的父级类型作为返回结果。当有多个这样的工厂,这些工厂之间会生产出一系列不同的对象,这种情况下,我们可以定义一个抽象工厂来描述工厂的行为,即创建一系列对象的方法,再让具体的工厂类继承这个抽象工厂。也可以说是把一个系列的工作抽象出来一个抽象的类型,这个抽象的类型就是抽象工厂。

先来看一下结构图:

下面来看一下具体代码:
定义抽象类型
public interface Animal {
    void say();}

public interface Cat extends Animal {
}

public interface Dog extends Animal {
}

public interface Duck extends Animal {
}

定义抽象工厂接口
public interface AnimalFactory {
    /**     * 抽象工厂方法     * 特点与工厂方法一致:     * 创建了一个对象     * 返回类型为一个接口或者抽象类     * 有多个类实现了上述抽象类型     *     * @param animalType     * @return     */    Animal getAnimal(String animalType);}

抽象类型的具体类与具体工厂类(US)
public class CatUs implements Cat {
    @Override    public void say() {
        System.out.println("america cat");    }
}

public class DogUs implements Dog {
    @Override    public void say() {
        System.out.println("america dog");    }
}

public class DuckUs implements Duck {
    @Override    public void say() {
        System.out.println("america duck");    }
}

public class AnimalFactoryUs implements AnimalFactory {
    @Override    public Animal getAnimal(String animalType) {
        if ("dog".equals(animalType)) {
            return new DogUs();        } else if ("cat".equals(animalType)) {
            return new CatUs();        } else if ("duck".equals(animalType)) {
            return new DuckUs();        } else {
            return null;        }
    }
}

抽象类型的具体类与具体工厂类(CHINA)
public class CatChina implements Cat {
    @Override    public void say() {
        System.out.println("china duck");    }
}

public class DogChina implements Dog {
    @Override
    public void say() {
        System.out.println("china dog");    }
}

public class DogChina implements Dog {
    @Override
    public void say() {
        System.out.println("chinese dog");    }
}

public class DuckChina implements Duck {
    @Override    public void say() {
        System.out.println("china cat");    }
}

public class AnimalFactoryChina implements AnimalFactory {
    @Override    public Animal getAnimal(String animalType) {
        if ("dog".equals(animalType)) {
            return new DogChina();        } else if ("cat".equals(animalType)) {
            return new CatChina();        } else if ("duck".equals(animalType)) {
            return new DuckChina();        } else {
            return null;        }
    }
}

测试主类
public class AnimalfactoryDemo {
    public static void main(String[] args) {
        AnimalFactoryChina animalFactoryChina = new AnimalFactoryChina();        animalFactoryChina.getAnimal("dog").say();
        AnimalFactoryUs animalFactoryUs = new AnimalFactoryUs();        animalFactoryUs.getAnimal("dog").say();    }
}

代码地址
https://github.com/angelala00/learn/tree/master/src/main/java/cn/jc/designpattern/abstractfactory

再多说一点
在研究抽象工厂的时候,也参考了多个网上搜索到的文章,记得有一篇是https://www.runoob.com/design-pattern/abstract-factory-pattern.html,看过之后总觉得不是很理想,文章中的代码看起来也是抽象工厂的逻辑和结构,然而某一个具体的工厂却有闲置的方法,比如ShapeFactory的getColor方法,这样在使用者看来,总会产生一些困惑,所以又找了纸质书资料,最终参考了《设计模式Java手册》中关于抽象工厂模式的讲解,并且把其中的代码实现了(地址:https://github.com/angelala00/learn/tree/master/src/main/java/cn/jc/designpattern/abstractfactory1)之后,觉得这里说的案例,更能准确的描述抽象工厂。于是有了本文的Demo。

JDK中抽象工厂的身影:
java.sql.Driver的connect方法。
Connection为connect方法返回的抽象类型。在java.sql.Driver中,没有给出具体的工厂类,而是由DriverManager来管理,动态注册到一个工厂集合中。



Thursday, May 9, 2019

设计模式-工厂模式

    工厂模式,也叫工厂方法模式,定义一个用于创建对象的接口,可以控制对哪个类进行实例化



public interface Animal {
    void say();
}



public class Cat implements Animal {
    @Override
    public void say() {
        System.out.println("miao miao miao");
    }
}




public class Dog implements Animal {
    @Override
    public void say() {
        System.out.println("wang wang wang");
    }
}




public class Duck implements Animal {
    @Override
    public void say() {
        System.out.println("ga ga ga");
    }
}



public class AnimalFactory {
    /**
     * 此方法为工厂方法模式,
     * 特点为:
     * 创建了一个对象
     * 返回类型为一个接口或者抽象类
     * 有多个类实现了上述抽象类型
     * @param color
     * @return
     */
    public Animal getAnimal(String color) {
        if ("dog".equals(color)) {
            return new Dog();
        } else if ("cat".equals(color)) {
            return new Cat();
        } else if ("duck".equals(color)) {
            return new Duck();
        } else {
            return null;
        }
    }
}

Demo入口


public class AnimalFactoryDemo {
    public static void main(String[] args) {
        AnimalFactory af = new AnimalFactory();
        Animal cat = af.getAnimal("cat");
        cat.say();
        Animal dog = af.getAnimal("dog");
        dog.say();
        Animal duck = af.getAnimal("duck");
        duck.say();
    }
}



工厂模式的使用场景
需要大量创建对象的工作,不同条件下要创建不同的对象,这些对象都是一个抽象类型的实现类对象

JDK中工厂模式的身影:
《设计模式Java手册》一书中提到,Iterable的子类型的iterator方法是典型的工厂方法模式的应用。我的理解,这里更应该是一个抽象工厂,当然,针对某一个具体的类,比如ArrayList,来说,它的iterator()方法,就是一个工厂方法。


    /**
     * JDK中工厂方法模式,iterator方法
     * 来自《设计模式Java手册》
     * @param args
     */
    public static void main2(String[] args) {
        List list = Arrays.asList(new String[]{"funasdf", "rocsdfd", "sparkkkasf"});
        Iterator i = list.iterator();
        while (i.hasNext()) {
            System.out.println(i.next());
        }

    }

觉得这里的理解还是有些欠缺,等有新的收获再来回顾。

Wednesday, May 8, 2019

关于“复用”的一点想法

       关于复用,如何合理的复用这个事,想说清楚,真不是那么容易的事情,下面我说一个表是否复用的大致场景,以及我的感想。
有一个现有的表,然后有一个新业务,这个新业务和原来的表对应的业务很像(可想象空间很大),
那应该不应该复用,这里可能很难下定论,因为不知道这两个业务在长远看来是否是有共性,是否是可合并的,是否是互斥的,等等因素
如果仅仅是为了少建表这一个原因的复用,那么我认为是不可取的,这种情况下,可能表的字段满足当前的需求,但字段意义是有着千差万别的
很多情况下,为了区分这两个业务,可能会用type这类字段来区分一下,然后很大概率可能发生的情况是某一个业务需要调整,增加了一个字段,这时候另一个业务是完全不需要的,长此以往,可想而知这个表将来是什么样子,至少我看到这个表,以及对应的多个业务,是想吐的。
当然也有可能在刚开始复用的时候,是判断不出后面是这样的场景,那么在扩展表的时候,就需要有一定的决断力,表该拆的时候,一定要拆,不要为了省事,凑合着用。
往往实际开发的时候,不是这样,拆分可能会带来额外的工作量以及风险,工作量会因人而异,如果代码质量好,拆分的工作量可能会低些,然后另一个大家都不愿意触碰的是风险,如果因这些改动而造成了一些问题,那这个问题谁来担,公司中因为这个因素,使得大家几乎没有任何动力去优化当前现有的难看的代码,除非是公司文化使然,或者是领导明确了优化范围以及提供相应的测试资源,明确了此次需求的责任人,把这个事当成一个项目去做,可是,这样不对么?对么?
就我的意愿来说,我是希望在每次开发迭代中,把重构放第一位,从整体到局部,这样来说,是否复用的问题就变成如何设计当前的项目问题了。
可是反过来我又问自己,多出来的时间怎么办?风险谁来把控?
我自己尝试着回答,如果从长期来看,在一直保持着持续重构的基础之上,每次多出来的时间与风险,是完全可以抵消下一次迭代的开发周期节约的时间成本以及后期查问题的维护成本,
这个收益是延迟的,而且在一个本来就很乱的基础之上,首次做重构的工作,是很难的,如果后期不能持续这个传统,那么这一次艰难的重构工作会变得费力不讨好的事情。

还有一个点是,在重构的过程,最好是相关的业务人一起讨论,产品或者功能的性质,以后的演变的可能性,技术实现方案能支持到什么程度,等等,一方面有助于相关的业务人理解业务,另一方面也让PM能够重新审视自己的产品是否有问题。 
写这个,也是因为公司代码中看到太多因为不合理的复用而导致的代码烂到不行,一边吐槽,一边梳理一下自己的想法。

Tuesday, May 7, 2019

设计模式-单例模式

        设计模式,通常指GOF的《设计模式 可复用面向对象软件的基础》书中提到的面向对象编程23种设计模式,话不多说,先来看一个单例模式。

单例模式,可以保证在系统中单例类只能有一个实例。

接下来我把在网上流行的大部分实现方案以及个人在思考单例模式的实现过程整理如下
方案一:

/**
 * 饿汉式
 * 使用时直接使用静态变量,理念不好,不利于扩展
 */
public class Singleton {
    /**
     * 初始化动作写到静态代码块中是一样的效果
     */
    public static Singleton instance = new Singleton();

    private Singleton() {
    }
}


方案二:

/**
 * 饿汉式
 * 使用时调用 getInstance 方法,后期如有需求可直接改动方法实现即可
 */
public class Singleton {
    public static Singleton instance = new Singleton();

    private Singleton() {
    }

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


方案三:

/**
 * 懒汉式,使用时才初始化
 * 线程不安全,单线程环境下可用
 */
public class Singleton {
    public static Singleton instance = null;

    private Singleton() {
    }

    public static Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
} 


方案四:

/**
 * 懒汉式,使用时才初始化
 * 线程安全,方法加锁,效率低
 */
public class Singleton {
    public static Singleton instance = null;

    private Singleton() {
    }

    public static synchronized Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}
 

方案五:

/**
 * 懒汉式,使用时才初始化
 * 线程安全,代码块加锁,效率高,
 */
public class Singleton {
    /**
     * 这里如果不加volatile声明,可能会因为JVM对指令重排导致异常
     */
    public static volatile Singleton instance = null;

    private Singleton() {
    }

    /**
     * 这里如果只判断里层,那和方法加锁一样了,如果只判断外层,那线程安全的问题没有解决
     * 这种方案可能在实例初始化时,如果遇到多线程问题,会有资源竞争,
     * @return
     */
    public static Singleton getInstance() {
        if (instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}


方案六:

/**
 * 静态内部类实现,针对内部类SingletonHolder来看,属于饿汉式,针对外部类Singleton来看,属于懒汉式的效果
 * 只有在调用getInstance方法的时候才会初始化内部类SingletonHolder的属性也就是单例实例
 * 由JVM帮我们保证了线程安全,JVM在初始化类的时候是不允许多线程进入的
 */
public class Singleton {
    public static String someStaticProperty = null;

    private Singleton() {
    }

    public static Singleton getInstance() {
        return SingletonHolder.INSTANCE;
    }

    private static class SingletonHolder {
        private static final Singleton INSTANCE = new Singleton();
    }
}
 

方案七:

/**
 * 用枚举来实现单例,有很多优点,无需考虑线程安全问题,还可以防止反序列化重新创建对象。
 * 枚举来作为单例的实现方案最初看到是在EffactiveJava中提出的,但没有被普遍使用该方案
 * 个人认为原因,枚举出来的比较晚,大家不是很熟悉,枚举是属于懒汉式
 * 对枚举的解读参考文章:https://www.cnblogs.com/kailejun/p/6624471.html
 */
public enum Singleton {
    INSTANCE;

    public void someMethod() {

    }
}


代码地址:
https://github.com/angelala00/learn/tree/master/src/main/java/cn/jc/designpattern/singleton

方案五和方案六是多线程环境下用的最普遍的两种方案,大部分人更认可的是方案六。

JDK中单例模式的身影:
java.long.Runtime

前提知识,面向对象编程

Saturday, May 4, 2019

排序算法演示动画

        写前面两篇排序的时候,百度找图片,看到还比较不错的动图,能够展示数据在内存中的变化。然而性格使然,我会仔细的观察动图,仔细观察的话发现要么是无法较好的展示比较过程,要么是交换的过程与实际代码执行的有一些出入,心想自己做一个效果出来。这一想不要紧,4月30号-5月4号,花了几个晚上时间,终于搞定了俩算法的排序效果动图,这效率低的也是没谁了。

中间的尝试过程,以及遇到的问题,
1,开始用JavaScriptr的canvas,画出了初始化的状态,做到动效觉得会很麻烦,卡住了。
2,咨询前端同事,建议用css,于是改用div,做到动效的时候,仍然卡住,原因是延迟函数执行的效果和预期不一致,大致能猜到js的闭包特性导致。
3,于是想到用Java的canvas直接画出来,于是乎动手,写到动效,又卡住,动效过程中及时渲染没搞定,无奈对API不熟悉,暂时放弃。
4,重新思考div方案中遇到的问题的解决办法,按说,仔细处理一下延迟方法,应该可行,于是再次动手,这次把冒泡的效果做出来了,想着没问题了,接着套入插入排序,然而执行效果又与预期不一致,究其原因发现插入排序中多了break导致,break无法让事先定时的timer取消往下执行,这下又卡住了,带着问题入睡。
5,早上再继续,想到把排序过程和效果分开来写(最开始就是分开写的,过程中觉得直接一边排序一边展示效果更合理,然而后来证明,分开也有分开的好处),这下可以了。

中间遇到问题原因之一是对前端展示部分的代码熟悉程度不够,二是没有整体从开始思考可行的方案,也是因对前端代码的评估过于简单了,只在脑海中想了一下,就觉得没问题。JS的canvas,CSS的动画,Java的Canvas,应该都能实现该效果,这里暂且不深究了,本来的目的也只是想在做排序的时候,可以做出辅助理解的动画效果。


冒泡排序
https://github.com/angelala00/learn/blob/master/src/main/html/sort/bubblesort.html

插入排序
https://github.com/angelala00/learn/blob/master/src/main/html/sort/insertionsort.html



Popular Posts

Copyright © 虫虫的成长历程 | Powered by Blogger Design by PWT | Blogger Theme by NewBloggerThemes.com