デザインパターン:設計の原則とデザインパターンを作成します

最近の研究で、「Javaのデザインパターン」(劉魏版)、デザインパターンの新しい理解に、今、デザインの原則とデザインパターンの6種類を記録する最初に深い印象を、独自の研究ノートを整理します。
間違っている場合は、私を修正してください!

設計の原則

単一責任の原則

シングル責任原則は、対応する義務はそれがガイドラインの範囲内の高凝集や低カップリングを達成することである機能領域に対してのみ責任があるクラスを参照します。

displayChart CreateChartは、表示アイコンを作成するために使用されると、上記に示したクラスのgetConnectionデータベース接続、ユーザーのリストについてfindCustomersをCustomerDataChart。このようなCustomerDataChartクラスは、機能の複数のタイプを含む単一責任の原則に準拠していません。
; CustomerDAO責任CRUDユーザ、findCustomersを含む方法、グラフを生成して表示するためCustomerDataChart責任、およびcreateChartを含むdisplayChart方法データベースの責任DBUTIL接続、getConnectionメソッドを含む単一責任の原則に従った再構成後、三つのカテゴリーを達成することができます。

開閉の第二、原則

オープンとの原則を閉じるには実体が拡張のために開いている必要がありますソフトウェアを指しますが、変更のため閉鎖しました。これは、ソフトウェアエンティティが前提の下でソースコードを変更せずに延長すべきです。PS:他のソフトウェアまたはプロパティのXML設定ファイルコンパイルせず、プレーンテキストファイルですので、設定ファイルについては、変更開閉の原則に沿ったものです。
重要な原則は、単にソースコードを変更せずに、達成するために拡張およびサブクラスを追加し、抽象クラスを介して抽象化の開閉、すなわち、サブクラス化することによって、特定の機能を実現するためのインタフェースまたは良い関数を定義することを、その後のメンテナンス拡張するようですこれ開閉の原則に沿っインチ

図は、開口部の原則に沿ったものであり、1つのデザインのアイデア、抽象AbstractChartクラスの定義を閉じると、表示方法を定義し、法によって決定ChartDisplayは、コールchart.displayの表示に、円グラフBarChartコントロールなどの具象サブクラスをsetChartサブクラス表示メソッド呼び出し、あなたが拡張を追加する必要がある場合は、新しいクラス図を作成する必要があるだろう、と具体的な表示方法は、ソースコードを変更せずに実施することができます。

第三に、リヒターの置換原則

あるいはLeebの原理は、そのサブクラスの明確でなければならない参照されたオブジェクトの基本クラスへの参照を指します。今後の親サブクラスを交換して、プログラムは、親クラスがサブクラスではない置き換えられます逆に、例外が発生することはありません。

  1. すべてのサブクラスは、親クラスで宣言されなければならない、または親クラスのサブクラスのすべてが実装する必要があります。サブクラスは親クラスのメソッドを実装している場合、拡張を容易にするために、親クラスの定義として、特定のサブクラスのメソッド呼び出しすることはできません、ではありません。
  2. リヒター置換原理を使用する場合は、親クラスにしようとインターフェースするように設計または抽象クラスなので、親クラスまたはサブクラスは、親クラスがインタフェースを実装継承し、親クラスの実装で宣言されています。
    PS:リヒターの置換原則は開閉の原則の具体的な実装です。

    第四に、依存関係逆転の原則

    依存関係逆転の原則を詳細に依存すべきではない抽象を意味し、詳細が抽象的に依存しなければなりません。プログラミングのための、というよりも、プログラミングインターフェイスに。
    ほとんどの場合、開閉の原則、リヒターの置換原則、依存性逆転の原則が同時に発生し、3人はちょうど、同じ問題の異なる視点を考え、目的のお互い、団結を補完します。

    第五に、インタフェースの分離の原則

    接口隔离原则:使用多个专门的接口,而不是使用一个总接口,即客户端不应该依赖那些不需要的接口。

    六、最少知识原则

    一个软件应该尽可能少的与其他实体发生关系。

    七、合成复用原则

    尽量使用对象组合,而不是继承来实现复用的目的。

    六个创建型设计模式

    一、简单工厂模式

    简单工厂模式:定义一个工厂类,它可以根据参数的不同返回不同的事例,被定义的事例大多都有共同的父类。

    简单工厂模式包含以下角色:
  3. Product(抽象产品角色):工厂所创建的角色抽象出来的抽象对象,是创建角色的父类,封装了角色的各种方法。
  4. ConcreateProduct(具体产品角色):简单工厂模式创建的具体目标。
  5. Factory(工厂角色):工厂类,负责创建所有产品角色的逻辑。在工厂类中实现了静态工厂方法,返回类型为Product,所以简单工厂模式又被称为 静态工厂模式

    具体使用范例:
    客户要求展示柱状图、饼状图、折线图的一种,根据传入的参数进行选择,如果没有使用设计模式,则实现过程如下。
class Chart {
            private String type; //图表类型

            public Chart(Object[][] data, String type) {
                this.type = type;
                if (type.equalsIgnoreCase("histogram")) {
                //初始化柱状图
                } else if (type.equalsIgnoreCase("pie")) {
                //初始化饼状图
                } else if (type.equalsIgnoreCase("line")) {
                //初始化折线图
                }
            }

            public void display() {
                if (this.type.equalsIgnoreCase("histogram")) {
                //显示柱状图
                } else if (this.type.equalsIgnoreCase("pie")) {
                //显示饼状图
                } else if (this.type.equalsIgnoreCase("line")) {
                //显示折线图
                }
            }
        }

代码中的问题有:

  1. 包含太多的if...else...,代码冗长,阅读难度大;
  2. Chart类职责过重,将对象的判断、初始化、展示全部放在一个类中,不符合单一职责原则;
  3. 如果需要增加新表,需要修改Chart类源代码,不符合开闭原则;
  4. 对象通过new实例化,类与客户端的耦合性太高,创建与使用无法分离;
    按照简单工厂模式进行重构:
  5. 将对象类抽象出一个抽象产品类,可以是抽象类,也可以是接口
interface Chart {
    public void display();
}
  1. 具体产品类实现抽象接口类,并实现具体的初始化以及展示方法
//柱状图类:具体产品类
        class HistogramChart implements Chart {
            public HistogramChart() {
                System.out.println("创建柱状图!");
            }
            public void display() {
                System.out.println("显示柱状图!");
            }
        }
  1. 创建工厂类,根据需求初始化对象,其中包含了 静态 的工厂方法,用于创建对象
//图表工厂类:工厂类
        class ChartFactory {
            //静态工厂方法
            public static Chart getChart(String type) {
                Chart chart = null;
                if (type.equalsIgnoreCase("histogram")) {
                    chart = new HistogramChart();
                    System.out.println("初始化设置柱状图!");
                }
                else if (type.equalsIgnoreCase("pie")) {
                    chart = new PieChart();
                    System.out.println("初始化设置饼状图!");
                }
                else if (type.equalsIgnoreCase("line")) {
                    chart = new LineChart();
                    System.out.println("初始化设置折线图!");
                }
                return chart;
            }
        }
  1. 客户端直接使用工厂类进行对象的初始化
class Client {
            public static void main(String args[]) {
                Chart chart;
                chart = ChartFactory.getChart("histogram"); //通过静态工厂方法创建产品
                chart.display();
            }
        }
  1. 第三步中判断客户端的参数来进行判断初始化,如果需要增加新的对象,那么需要修改工厂类的源代码,不符合开闭原则,可以通过配置文件进行优化(根据开闭原则的定义,配置文件的修改不算是违背开闭原则)。
    在xml配置文件中定义键值对,通过DOM读取文件中的对象名,通过Java反射获取制定的对象事例。
public static Object getBean() {
            try {
                //创建DOM文档对象
                DocumentBuilderFactory dFactory = DocumentBuilderFactory.newInstance();
                DocumentBuilder builder = dFactory.newDocumentBuilder();
                Document doc;
                doc = builder.parse(new File("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;
            }
        }

缺点:

  1. 只有一个工厂类,职责过重;
  2. 引入新的类(工厂类、抽象对象类),增加了系统复杂度;
  3. 仍不符合开闭原则;
  4. 工厂方法使用了静态方法,工厂类无法形成继承结构。

    二、工厂方法模式

    工厂方法模式:定义一个用于创建对象的接口,让子类决定具体创建哪个对象。

    工厂方法模式包含的角色:
  5. Product(抽象产品):定义产品的接口,所有产品对象的超类型;
  6. ConcreateProduct(具体产品):需要创建的产品对象;
  7. Factory(抽象工厂):定义工厂的接口,所有具体工厂的超类型;
  8. ConcreateFactory(具体工厂):抽象工厂的子类,实现了抽象工厂中的工厂方法,与ConcreateProduct是一对一关系,由客户端调用。

    具体使用范例:
    以前面的简单工厂模式为例,需要对简单工厂模式重构的代码进行再次重构。
  9. 将对象类抽象出一个抽象产品类,可以是抽象类,也可以是接口
interface Chart {
    public void display();
}
  1. 具体产品类实现抽象接口类,并实现具体的初始化以及展示方法
//柱状图类:具体产品类
        class HistogramChart implements Chart {
            public HistogramChart() {
                System.out.println("创建柱状图!");
            }
            public void display() {
                System.out.println("显示柱状图!");
            }
        }
  1. 创建抽象工厂类
//抽象工厂类
interface ChartFactory{
        public Chart createChart();
    }
  1. 创建具体工厂类
//图表工厂类:具体工厂类
class HistogramChartFactory implements ChartFactory{
        @Override
        public Chart createChart() {
            Chart chart = new HistogramChart();
            return chart;
        }
    }
  1. 客户端直接使用工厂类进行对象的初始化
class Client {
        public static void main(String args[]) {
            ChartFactory factory;
            Chart chart;
            factory = new HistogramChartFactory(); //可引入配置文件实现
            chart = factory.createChart();
            chart.display();
        }
    }
  1. 第五步中判断客户端的参数来进行判断初始化,如果需要增加新的对象,那么需要修改工厂类的源代码,不符合开闭原则,可以通过配置文件进行优化(根据开闭原则的定义,配置文件的修改不算是违背开闭原则)。
    在xml配置文件中定义键值对,通过DOM读取文件中的对象名,通过Java反射获取制定的对象事例。
public static Object getBean() {
            try {
                //创建DOM文档对象
                DocumentBuilderFactory dFactory = DocumentBuilderFactory.newInstance();
                DocumentBuilder builder = dFactory.newDocumentBuilder();
                Document doc;
                doc = builder.parse(new File("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;
            }
        }

优点:

  1. 完全符合开闭原则;
    缺点:
  2. 添加新的对象时,需要创建对应的具体对象类与具体工厂类,系统中的类成对增加;
  3. 使用了DOM、Java反射,增加了系统实现难度。

    三、抽象工厂模式

    抽象工厂模式:提供一个创建一系列相关或相互依赖对象的接口,而无须指定它们具体的类。
    抽象工厂模式中包含的角色:
  4. Factory(抽象工厂):声明了一组用于创建一族对象的方法,一个方法对应一类对象;
  5. ConcreateFactory(具体工厂):实现了抽象工厂中具体创建对象的方法,生成一些具体产品对象;
  6. Product(抽象对象):为每种产品声明接口,所有产品对象的超类型;
  7. ConcreateProduct(具体对象):具体的产品对象。

    具体使用范例:
    以上述的工厂方法模式为例,现有柱状图、圆饼图、折线图,如果要分别增加红色、白色、黑色三种颜色,按照工厂方法模式,需要建立9种具体工厂类,如果按照抽象工厂模式,只需要三种,代码需要重构。
  8. 将对象类抽象出一个抽象产品类,可以是抽象类,也可以是接口
interface HistogramChart {
    public void display();
}
interface PieChart {
    public void display();
}
interface LineChart {
    public void display();
}
  1. 具体产品类实现抽象接口类,并实现具体的初始化以及展示方法
//柱状图类:具体产品类
class RedHistogramChart implements HistogramChart {
    public RedHistogramChart() {
        System.out.println("创建红色柱状图!");
    }
    public void display() {
        System.out.println("显示红色柱状图!");
    }
}
...
  1. 创建抽象工厂类
//抽象工厂类
interface ChartFactory{
        public HistogramChart createHistogramChart();
        public PieChart createPieChart();
        public LineChart createLineChart();
    }
...
  1. 创建具体工厂类
//图表工厂类:具体工厂类
class RedChartFactory implements ChartFactory{
        @Override
        public HistogramChart createHistogramChart() {
            HistogramChart chart = new RedHistogramChart();
            return chart;
        }
        @Override
        public PieChart createPieChart() {
            PieChart chart = new RedPieChart();
            return chart;
        }
        @Override
        public LineChart createLineChart() {
            LineChart chart = new RedLineChart();
            return chart;
        }
    }
...
  1. 客户端直接使用工厂类进行对象的初始化
class Client {
        public static void main(String args[]) {
            //使用抽象层定义
            ChartFactory factory;
            HistogramChart hc;
            PieChart pc;
            LineChart lc;
            factory = (ChartFactory)XMLUtil.getBean();
            hc = factory.createHistogramChart();
            pc = factory.createPieChart();
            lc = factory.createLineChart();
            hc.display();
            pc.display();
            lc.display();
        }
    }

缺点:
结构复杂,重构工作量大。

四、单例模式

单例模式:确保某一个类只有一个实例,而且自行初始化并向整个系统提供这个实例。
单例模式中的角色:

  1. singleton(单例):在单例类的内部实现只生成一个实例,同时它提供一个静态的getInstance()工厂方法,让客户可以访问它的唯一实例;为了防止在外部对其实例化,将其构造函数设计为私有;在单例类内部定义了一个Singleton类型的静态对象,作为外部共享的唯一实例。

单例模式的实现步骤:

  1. 单例模式只允许存在一个实例,由于new方法每次都能初始化一个实例,所以应将对象的构造方法设为private;
  2. 由该私有构造方法构造的唯一实例需要向整个系统公开,所以为了保证唯一性,需要定义一个静态私有变量来存放这个实例;
  3. 为了向系统公开,定义一个公开的getInstance方法,并返回第二步的静态私有变量。

在getInstance方法中,为了向系统提供唯一的实例,需要先判断实例是否初始化了,如果有则返回,如果没有则初始化并返回。当不同线程同时调用该方法时,两个判断语句都会返回false,并且同时初始化实例,导致系统不止有一个实例,引起异常。因此需要对单例模式进行线程安全操作,常见的解决办法有饿汉式、懒汉式、IoDH。

饿汉式

在私有静态变量定义的时候进行初始化,这样在类加载的时候就已经创建了单例,只需要在getInstance中返回该单例就可以了。

private static final EagerSingleton instance = new EagerSingleton();

优点:在类加载时初始化,无需考虑多线程,可保证只有一个实例,从调用速度跟反应时间来说是最优解决方案。
缺点:无论是否使用,在类加载时都会初始化对象,从资源利用率上来说不如懒汉式,且软件加载时间会变长。

懒汉式

在私有静态变量定义的时候不进行初始化,而是在getInstance方法中进行线程保护,可以为etInstance方法添加 synchronized 修饰符,这样在不同线程同时调用getInstance时,就会依次进行访问。
为getInstance方法添加synchronized修饰符后,每次调用方法都会进行同步锁的判定,会消耗大量的系统资源,因此可以对懒汉式进行优化,去掉getInstance方法的synchronized修饰符,只对方法内的初始化方法进行锁定,这样只有在第一次调用时进行同步锁判定,之后都不会影响性能。

if (instance == null) {
    synchronized (LazySingleton.class) {
    instance = new LazySingleton();
    }
}

使用以上优化后,表面上是对线程同步做了保护,但实际上仍会出现多个单例对象出现的现象。假如在某一瞬间线程A和线程B都在调用getInstance()方法,此时instance对象为null值,均能通过instance == null的判断。由于实现了synchronized加锁机制,线程A进入synchronized锁定的代码中执行实例创建代码,线程B处于排队等待状态,必须等待线程A执行完毕后才可以进入synchronized锁定代码。但当A执行完毕时,线程B并不知道实例已经创建,将继续创建新的实例,导致产生多个单例对象,违背单例模式的设计思想,因此需要进行进一步改进,在synchronized中再进行一次(instance == null)判断,这种方式称为双重检查锁定(Double-CheckLocking)。使用双重检查锁定实现的懒汉式单例类完整代码如下所示:

private volatile static LazySingleton instance = null;
public static LazySingleton getInstance() {
    //第一重判断
    if (instance == null) {
        //锁定代码块
        synchronized (LazySingleton.class) {
            //第二重判断
            if (instance == null) {
            instance = new LazySingleton(); //创建单例实例
            }
        }
    }
    return instance;
}

需要注意的是,如果使用双重检查锁定,需要在静态变量前面添加volatile修饰符,以便成员变量可以在多个线程中被正确处理。
优点:实例在第一次使用时创建,无需一直占用系统资源,实现了延迟加载。
缺点:多线程问题需要进行处理,在第一次初始化时可能占用大量资源进行判断。

IoDH

在单例类中增加一个静态(static)内部类,在该内部类中创建单例对象,再将该单例对象通过getInstance()方法返回给外部使用。

private Singleton() {
}
private static class HolderClass {
    private final static Singleton instance = new Singleton();
}
public static Singleton getInstance() {
    return HolderClass.instance;
}
public static void main(String args[]) {
    Singleton s1, s2;
    s1 = Singleton.getInstance();
    s2 = Singleton.getInstance();
    System.out.println(s1==s2);
}

在第一次调用时进行初始化,通过静态内部类HolderClass进行线程保护,因为HolderClass中的instance是静态成员变量,所以该变量的线程保护由Java虚拟机来保证其安全性,因此性能不会受到影响。
优点:从调用速度、资源利用率来说都是最优选择。
缺点:有些语言不支持。

五、原型模式

原型模式:使用原型实例创建对象的种类,并且通过拷贝这些原型创建新的对象。
原型模式包含的角色:

  1. Prototype(抽象原型类):它是声明克隆方法的接口,是所有原型类的公共父类,可以是抽象类也可以是接口,甚至可以是具体实现类。
  2. ConcretePrototype(具体原型类):它实现在抽象原型类中声明的克隆方法,在克隆方法中返回一个自己的克隆对象。
  3. Client(客户类):让一个原型对象克隆自身从而创建一个新的对象,在客户类中只需要直接实例化或通过工厂方法等方式创建一个原型对象,再通过调用该对象的克隆方法即可得到多个相同的对象。

    实现方法一:通用实现方法
    在具体原型类的克隆方法中实例化一个与自身类型相同的对象并将其返回,并将相关的参数传入新创建的对象中。
public Prototype clone() //克隆方法
{
    Prototype prototype = new ConcretePrototype(); //创建新对象
    prototype.setAttr(this.attr);
    return prototype;
}
实现方法二:Java语言提供的clone方法

所有Java类都继承自java.lang.Object,Object类提供了一个clone方法,可以将Java对象复制一份。需要注意的是能够实现克隆的Java类必须实现Cloneable接口,否则会报异常。

class ConcretePrototype implements Cloneable
{
……
}

克隆方法分为深克隆跟浅克隆,主要区别在于是否支持引用类型的成员变量的复制。

浅克隆

在浅克隆中,如果原型对象的成员变量是值类型,将复制一份给克隆对象,如果是引用类型,则将引用对象的地址复制一份给克隆对象。原型模型的第二种方式通过覆盖Java中Object类中的clone方法实现的克隆就是浅克隆。

深克隆

在深克隆中,无论原型对象的成员变量是值类型还是引用类型,都会复制一份给克隆对象。如果要实现深克隆,需要使用原型模式的第一种实现方式,通过序列化进行实现。所以需要深克隆的对象必须实现Serialiazable接口。

public WeeklyLog deepClone() throws IOException, ClassNotFoundException, OptionalDataException
{
    //将对象写入流中
    ByteArrayOutputStream bao=new ByteArrayOutputStream();
    ObjectOutputStream oos=new ObjectOutputStream(bao);
    oos.writeObject(this);
    //将对象从流中取出
    ByteArrayInputStream bis=new ByteArrayInputStream(bao.toByteArray());
    ObjectInputStream ois=new ObjectInputStream(bis);
    return (WeeklyLog)ois.readObject();
}

六、建造者模式

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

建造者模式中的角色:
Builder(抽象建造者):它为创建一个产品对象的各个部件制定抽象接口,该接口中一般有两类方法,一类是buildPartX,用于创建对象的各个部件;另一类是getResult,用于返回复杂对象;
ConcreteBuilder(具体创建者):实现了Builder接口,实现具体的内部构造和装配方法,并返回目标对象;
Product(产品角色):被构建的复杂对象;
Director(指挥者):又称为导演类,复杂安排复杂对象的建造次序,指挥者与抽象建造者之间存在关联关系,指挥者通过抽象建造者进行对象的创建。

class Director {
    private Builder builder;
    public Director(Builder builder) {
        this.builder=builder;
    }
    public void setBuilder(Builder builder) {
        this.builder=builer;
    }
    //产品构建与组装方法
    public Product construct() {
        builder.buildPartA();
        builder.buildPartB();
        builder.buildPartC();
        return builder.getResult();
    }
}

建造者模式跟抽象工厂模式有点类似,但建造者模式返回一个完整的复杂对象,抽象工程模式返回一系列对象。

おすすめ

転載: www.cnblogs.com/zhangzhonghao/p/10939606.html