设计模式总结(二)

本文首次发布于My Blog,作者@张琦(Ian),转载请保留原文链接。

装饰模式

装饰模式:动态地给一个对象添加一些额外的职责,就增加功能来说,装饰模式比生成子类更为灵活。

先上代码:

//义一个对象接口,可以给这些对象动态地添加职责。
public abstract class Component {
    public abstract void operation();
}
public class ConcreteComponent extends Component {
    @Override
    public void operation() {
        System.out.println("具体对象的操作");
    }
}
public class Decorator extends Component {
    protected Component component;

    //设置Component
    public void setComponent(Component component) {
        this.component = component;
    }

    //重写operation(),实际执行的是Component的operation()
    @Override
    public void operation() {
        if (null != component){
            component.operation();
        }
    }
}
public class ConcreteDecoratorA extends Decorator {
    private String addedState;

    @Override
    public void operation(){
        //首先运行原Component的operation(),再执行本类功能,如addedState,相当于对原Component进行了装饰
        super.operation();
        addedState = "New State";
        System.out.println("具体装饰对象A的操作");
    }
}

class ConcreteDecoratorB extends Decorator{
    @Override
    public void operation(){
        //首先运行原Component的operation(),再执行本类功能,如AddedBehavior,相当于对原Component进行了装饰
        super.operation();
        AddedBehavior();
        System.out.println("具体装饰对象B的操作");
    }

    public void AddedBehavior(){}
}
public class main {
    public static void main(String[] args){
        ConcreteComponent c = new ConcreteComponent();
        ConcreteDecoratorA d1 = new ConcreteDecoratorA();
        ConcreteDecoratorB d2 = new ConcreteDecoratorB();

        /**
         * 装饰的方法是:首先用ConcreteComponent实例化对象c,然后用ConcreteDecoratorA的实例化对象d1来包装c,
         * 再用ConcreteDecoratorB的对象d2包装d1,最终执行d2的operation();
         */
        d1.setComponent(c);
        d2.setComponent(d1);
        d2.operation();
    }
}

  利用setComponent来对对象进行包装,这样每个装饰对象的实现就和如何使用这个对象分离开了,每个装饰对象只关心自己的功能,不需要关心如何被添加到对象链当中。

总结

  总结:装饰模式是为已有功能动态地添加更多功能的一种方式。

  优点:把类中的装饰功能从类中搬移去除,这样可以简化原有的类。

  好处:有效的把类的核心职责和装饰功能区分开了,而且可以去除相关类中重复的装饰逻辑。

  注意:装饰的顺序很重要。
  
  

代理模式

代理模式: 为其他对象提供一种代理以控制对这个对象的访问。

先上代码:

//定义RealSubject 和 Proxy的公共接口,这样就可以使用RealSubject的地方都可以使用Proxy
public abstract class Subject {
    public abstract void request();
}
//定义Proxy所代表的真实实体
public class RealSubject extends Subject {
    @Override
    public void request() {
        System.out.println("真实的请求");
    }
}
//保存一个引用使得代理可以访问实体,并提供一个Subject的接口相同的接口,这样代理就可以用来替代实体。
public class Proxy extends Subject {
    RealSubject realSubject;

    @Override
    public void request() {
        if (null == realSubject){
            realSubject = new RealSubject();
        }
        realSubject.request();
    }
}
public class main {
    public static void main(String[] args) {
        Proxy proxy = new Proxy();
        proxy.request();
    }
}

应用场景:

  1. 远程代理:也就是为一个对象在不同的地址空间提供局部代表。这样可以隐藏一个对象存在于不同地址空间的事实。
  2. 虚拟代理:根据需要创建开销很大的对象。通过它来存放实例化需要很长时间的真实对象。
  3. 安全代理:用来控制真实对象访问时的权限。
  4. 智能指引:指当调用真实的对象时,代理处理另外一些事。

  说白了,代理就是真实对象的代表。

工厂方法模式

工厂方法模式:定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其它子类。

public class LeiFeng {
    public void sweep(){
        System.out.println("扫地");
    }

    public void wash(){
        System.out.println("洗衣");
    }

    public void buyRice(){
        System.out.println("买米");
    }
}
//雷锋大学生
public class Undergraduate extends LeiFeng {

}
//志愿者
public class Volunteer extends LeiFeng {
}
//雷锋工厂
public interface IFactory {
    LeiFeng CreateLeiFeng();
}
//学雷锋大学生工厂
public class UndergraduateFactory implements IFactory {
    //返回雷锋大学生
    @Override
    public LeiFeng CreateLeiFeng() {
        return new Undergraduate();
    }
}

//社区志愿者工厂
class VolunteerFactory implements IFactory {
    //返回志愿者
    @Override
    public LeiFeng CreateLeiFeng() {
        return new Volunteer();
    }
}
public class main {
    public static void main(String[] args) {
        IFactory factory = new UndergraduateFactory();  //如果换成自愿者,只需要修改这里就可以了
        LeiFeng student = factory.CreateLeiFeng();

        student.buyRice();
        student.sweep();
        student.wash();
    }
}

  我之前有说过简单工厂,这里我说说他们的区别:简单工厂模式最大的优点在于工厂类中包含了必要的逻辑判断,根据客户端的选择条件动态实例化相关的类,对于客户端来说,去除了与具体产品的依赖。

  工厂方法模式实现时,客户端需要决定实例化哪一个工厂来实现运算类,选择判断的问题还是存在的,也就是说 工厂方法把简单工厂的内部逻辑判断移到了客户端代码来进行。你想要加功能,本来说修改工厂类,而现在只需要修改客户端。
  缺点:由于每加一个产品,就需要加一个产品工厂的类,增加了额外的开发量。

  总结:工厂方法克服了简单工厂违背开放-封闭原则的缺点,又保持了封装对象创建过程的优点。

原型模式

原型模式:用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。

举例(浅克隆):

// 工作经历类
public class WorkExperience {
    private String workDate;
    private String company;

    public String getWorkDate() {
        return workDate;
    }

    public String getCompany() {
        return company;
    }

    public void setWorkDate(String workDate) {
        this.workDate = workDate;
    }

    public void setCompany(String company) {
        this.company = company;
    }
}
//简历类
public class Resume implements Cloneable {
    private String name;
    private String sex;
    private String age;

    //引用"工作经历"对象
    private WorkExperience work;

    //在"简历"实例化的同时实例化"工作经历"
    public Resume(String name) {
        this.name = name;
        work = new WorkExperience();
    }

    //设置个人信息
    public void setPersonalInfo(String sex, String age) {
        this.sex = sex;
        this.age = age;
    }


    //设置工作经历
    public void setWorkExperience(String workDate, String company) {
        work.setWorkDate(workDate);
        work.setCompany(company);
    }

    //显示
    public void display() {
        System.out.println("工作经历:" + work.getWorkDate() + work.getCompany());
    }

    @Override
    public Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}
public class main {
    public static void main(String[] args) throws CloneNotSupportedException {
        Resume a = new Resume("Ian");
        a.setPersonalInfo("男","26");
        a.setWorkExperience("时间年份","XX公司");

        Resume b = (Resume)a.clone();
        b.setWorkExperience("时间年份","XX企业");

        Resume c = (Resume)a.clone();
        c.setPersonalInfo("男","27");
        c.setWorkExperience("时间年份","ZZ企业");

        a.display();
        b.display();
        c.display();
    }
}

举例(深克隆):

//深克隆(深复制)
public class Resume implements Cloneable {
    private String name;
    private String sex;
    private String age;
    private WorkExperience work;

    public Resume(String name) {
        this.name = name;
        work = new WorkExperience();
    }

    private Resume(WorkExperience work) throws CloneNotSupportedException {
        this.work = (WorkExperience)work.clone();
    }

    //设置个人信息
    public void setPersonalInfo(String sex, String age) {
        this.sex = sex;
        this.age = age;
    }


    //设置工作经历
    public void setWorkExperience(String workDate, String company) {
        work.setWorkDate(workDate);
        work.setCompany(company);
    }

    //显示
    public void display() {
        System.out.println("工作经历:" + work.getWorkDate() + work.getCompany());
    }

    public Object clone() throws CloneNotSupportedException {
        Resume obj = new Resume(this.work);
        obj.name = this.name;
        obj.age = this.age;
        obj.sex = this.sex;
        return obj;
    }
}
// 工作经历类
public class WorkExperience {
    private String workDate;
    private String company;

    public String getWorkDate() {
        return workDate;
    }

    public String getCompany() {
        return company;
    }

    public void setWorkDate(String workDate) {
        this.workDate = workDate;
    }

    public void setCompany(String company) {
        this.company = company;
    }

    public Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

总结:

  浅克隆(浅复制):java必须让该类实现java.lang.Cloneable接口;重写(override)Object类的clone()方法;在派生类的clone()方法中调用super.clone();

  深克隆(深复制):除了对象本身被复制外,对象所有包含的所有成员变量也将复制。

模版方法模式

模版方法模式:定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模版方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。

例子:

//抽象模版
public abstract class AbstractClass {
    //一些抽象行为,放到子类去实现
    public abstract void primitiveOperation1();

    public abstract void primitiveOperation2();

    //模版方法,给出了逻辑的骨架,而逻辑的组成是一些相应的抽象操作,它们都推迟到子类实现
    public void templateMethod(){
        primitiveOperation1();
        primitiveOperation2();
        System.out.println("");
    }
}
public class ConcreteClassA extends AbstractClass {

    // A 和 B 的行为不一样
    @Override
    public void primitiveOperation1() {
        System.out.println("具体类A方法1的实现");
    }

    @Override
    public void primitiveOperation2() {
        System.out.println("具体A方法2的实现");

    }
}

class ConcreteClassB extends AbstractClass {
    // 与ConcreteClassA 不同的方法实现
    @Override
    public void primitiveOperation1() {
        System.out.println("具体B方法2的实现");
    }

    @Override
    public void primitiveOperation2() {
        System.out.println("具体B方法2的实现");
    }
}
public class main {
    public static void main(String[] args){
        AbstractClass c;

        c = new ConcreteClassA();
        c.templateMethod();

        c = new ConcreteClassB();
        c.templateMethod();
    }
}

应用场景:

  当我们要完成在某一细节层次一致的一个过程或一个系列步骤,但其个别步骤在更详细的层次上的实现可能不同时,我们就可以考虑用模版方法模式来处理。

优点:

  模版方法模式是通过把不变行为搬移到超类,去除了子类中的重复代码来体现它的优势。

总结:

  当不变的和可变的行为在方法的子类实现中混合在一起的时候,不变的行为就会在子类中重复出现。我们通过模版方法模式把这些行为搬移到单一的地方,这样就帮助子类摆脱重复的不变行为的纠缠。

迪米特法则

迪米特法则:如果两个类不必彼此直接通信,那么这两个类就不应当发生直接的相互作用。如果其中一个类需要调用另一个类的某一个方法的话,可以通过第三者转发这个调用。

  迪米特法则其根本思想,是强调了类之间的松耦合。类之间的耦合越弱,越有利于复用,一个处在弱耦合的类被修改,不会对有关系的类造成波及。
  

外观模式

外观模式:为子系统中的一组接口提供一个一致的界面,此模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。

先上代码:

public class SubSystemOne {
    public void methodOne(){
        System.out.println("子系统方法一");
    }
}

class SubSystemTwo{
    public void methodTwo(){
        System.out.println("子系统方法二");
    }
}

class SubSystemThreee{
    public void methodThree(){
        System.out.println("子系统方法三");
    }
}

class SubSystemFour{
    public void methodFour(){
        System.out.println("子系统方法四");
    }
}
//外观类,它需要了解所有的子类系统的方法或树形,进行组合,以备外界调用。
public class Facade {
    SubSystemOne one;
    SubSystemTwo two;
    SubSystemThreee threee;
    SubSystemFour four;

    public Facade(){
        one = new SubSystemOne();
        two = new SubSystemTwo();
        threee = new SubSystemThreee();
        four = new SubSystemFour();
    }

    public void methodA(){
        System.out.println("方法组A()-----");
        one.methodOne();
        two.methodTwo();
        threee.methodThree();
        four.methodFour();
    }

    public void methodB(){
        System.out.println("方法组B()-----");
        two.methodTwo();
        threee.methodThree();
    }
}
// 由于Facade的作用,客户端可以根本不知三个子系统类的存在。
public class main {
    public static void main(String[] args){
        Facade facade = new Facade();
        facade.methodA();
        facade.methodB();
    }
}

总结:

  外观模式完美的体现了依赖倒转原则和迪米特法则的思想(右侧有目录)。

应用场景:

  首先,在设计初期阶段,应该要有意识的将不同的两个层分离(比如我们经常使用的三层架构)。其次,在开发阶段,子系统往往因为不断的重构演化而变得越来越复杂,增加外观Facade可以提供一个简单的接口,减少它们之间的依赖。第三,在维护一个遗留的大型系统时,可能这个系统已经非常难以维护和扩展了,此时用外观模式Facade也是非常适合的。你可以为新系统开发一个外观Facade类,来提供设计粗糙或高度复杂的遗留代码的比较清晰的简单的接口,让新系统与Facade对象交互,Facade与遗留代码交互所有复杂的工作。

建造者模式

建造者模式:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。

继续先上代码:

// 产品类
public class Product {
    List<String> parts = new ArrayList<>();

    //添加产品部件
    public void addes(String part){
        parts.add(part);
    }

    public void show(){
        System.out.println("产品创建 ----");
        //列举所有的产品部件
        for (String part : parts){
            System.out.println(part);
        }
    }
}
//抽象建造者类,确定产品由两个部件PartA 和 PartB 组成,并声明一个得到产品建造后结果的方法getReult 
public abstract class Builder {
    public abstract void buildPartA();

    public abstract void buildPartB();

    public abstract Product getResult();
}
public class ConcreteBuilder1 extends Builder {
    private Product product = new Product();

    @Override
    public void buildPartA() {
        product.addes("部件a");
    }

    @Override
    public void buildPartB() {
        product.addes("部件b");
    }

    @Override
    public Product getResult() {
        return product;
    }
}
public class ConcreteBuilder2 extends Builder {
    private Product product = new Product();

    @Override
    public void buildPartA() {
        product.addes("部件a2");
    }

    @Override
    public void buildPartB() {
        product.addes("部件b2");
    }

    @Override
    public Product getResult() {
        return product;
    }
}
// 指挥者
public class Director {
    public void construct(Builder builder){
        //用来指挥建造过程
        builder.buildPartA();
        builder.buildPartB();
    }

}
// 客户端代码
public class main {
    public static void main(String[] args){
        Director director = new Director();
        Builder b1 = new ConcreteBuilder1();
        Builder b2 = new ConcreteBuilder2();

        // 指挥者用ConcreteBuilder1的方法来建造产品
        director.construct(builder1);
        Product p1 = builder1.getReult();
        p1.show();

        // 指挥者用ConcreteBuilder2的方法来建造产品
        director.construct(builder2);
        Product p2 = builder2.getReult();
        p2.show();
    }
}

应用场景:

  1.如果你需要将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示的意图时,我们就可以考虑使用建造者模式。

  2.它主要是用于创建一些复杂的对象,这些对象内部构建间的构造顺序通常是稳定的,但对象内部的构建通常面临着复杂的变化。

  3.建造者模式是当创建复杂对象的算法应该独立于该对象的组成部分以及它们的装配方式时适用的模式。

总结:

  建造者模式中有一个很重要的类,指挥类(Director)上述代码注释有写,用它来控制建造过程,也用它来隔离用户与建造过程的关联。
  

右侧可以看目录,点击直接跳转, 阿琦一共写了4篇(可以点击下面直接跳转然后看右侧目录):


每个人吸收知识的时候,都要有抽取精华,去除糟粕的能力。作者所说的,可能有些是对的,有些是错的,有些是适合你的,有些是不太适合你的,你要自己能够判断。

其实你在生活和工作当中也是一样的,你身边的人形形色色,有的人你喜欢,有的人你很讨厌。但其实你喜欢的人也有缺点,你讨厌的人也有优点。你要学会从你讨厌的人身上学会他的优点,千万不要一棒子打死,这只会让你失去很多学习成长的机会。

希望本文可以帮助到作为程序猿或即将成为程序猿的你。

原文地址:https://www.cnblogs.com/uniquezhangqi/p/9199214.html