深入浅出设计模式(八):10.合成模式(Composite) 11.享元模式(Flyweight)

10.合成模式(Composite)

合成模式又叫树形模式,就是把部分和整体的关系用树形结构表示,使得客户端对单个对象和组合对象的使用具有一致性。

哪里会使用到合成模式

比如将一个文件从C盘复制到D盘,也可以将一个文件夹从C盘复制到D盘中,不管是哪种方式,我们的操作都是一样的,而且文件夹下的所有文件都会同时复制过去。

合成模式的实现原理

这里写图片描述

合成模式在薪酬系统中的实际应用

这里写图片描述

代码比较简单,就不在此贴出,全部放在项目中

合成模式在JUnit的实际应用

比如一个TestCase实例测试只能针对独立一个测试类,如果有多个测试类,一个一个地执行也会耗费不少时间,所以JUnit也提供了TestSuite,可以一次执行多个测试类。

TestCase是对单个类编写的测试类,而TestSuite是对一组TestCase的集合进行测试,而且这个集合里面也可以包含TestSuite,这样运行一个TestSuite会将其包含的TestCase全部运行。不管是TestCase还是TestSuite,JUnit都会按照同样的方式执行,这就是合成模式。

下面贴出JUnit4.12源码

Test

package junit.framework;

public abstract interface Test
{
  public abstract int countTestCases();

  public abstract void run(TestResult paramTestResult);
}

TestCase

package composite;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

public abstract class TestCase extends Assert implements Test {
    //测试类名称
    private String fName;

    public TestCase() {
        this.fName = null;
    }

    public TestCase(String name) {
        this.fName = name;
    }

    //获取被run执行的测试案例的数量
    public int countTestCases() {
        return 1;
    }

    protected TestResult createResult() {
        return new TestResult();
    }

    public TestResult run() {
        TestResult result = createResult();
        run(result);
        return result;
    }

    public void run(TestResult result) {
        result.run(this);
    }

    //执行测试方法,包括初始化和销毁方法
    public void runBare() throws Throwable {
        Throwable exception = null;
        setUp();
        try {
            runTest();
            try {
                tearDown();
            } catch (Throwable tearingDown) {
                if (exception == null) {
                    exception = tearingDown;
                }
            }
            if (exception == null) {
                return;
            }
        } catch (Throwable running) {
            exception = running;
        } finally {
            try {
                tearDown();
            } catch (Throwable tearingDown) {
                if (exception == null) {
                    exception = tearingDown;
                }
            }
        }
        throw exception;
    }

    //执行测试方法
    protected void runTest() throws Throwable {
        assertNotNull("TestCase.fName cannot be null", this.fName);
        Method runMethod = null;
        try {
            //利用反射机制
            runMethod = getClass().getMethod(this.fName, (Class[]) null);
        } catch (NoSuchMethodException e) {
            fail("Method \"" + this.fName + "\" not found");
        }
        if (!Modifier.isPublic(runMethod.getModifiers())) {
            fail("Method \"" + this.fName + "\" should be public");
        }
        try {
            runMethod.invoke(this, new Object[0]);
        } catch (InvocationTargetException e) {
            e.fillInStackTrace();
            throw e.getTargetException();
        } catch (IllegalAccessException e) {
            e.fillInStackTrace();
            throw e;
        }
    }

    public static void assertTrue(String message, boolean condition) {
        Assert.assertTrue(message, condition);
    }

    public static void assertTrue(boolean condition) {
        Assert.assertTrue(condition);
    }

    public static void assertFalse(String message, boolean condition) {
        Assert.assertFalse(message, condition);
    }

    public static void assertFalse(boolean condition) {
        Assert.assertFalse(condition);
    }

    public static void fail(String message) {
        Assert.fail(message);
    }

    public static void fail() {
    }

    public static void assertEquals(String message, Object expected,
            Object actual) {
        Assert.assertEquals(message, expected, actual);
    }

    public static void assertEquals(Object expected, Object actual) {
        Assert.assertEquals(expected, actual);
    }

    public static void assertEquals(String message, String expected,
            String actual) {
        Assert.assertEquals(message, expected, actual);
    }

    public static void assertEquals(String expected, String actual) {
        Assert.assertEquals(expected, actual);
    }

    public static void assertEquals(String message, double expected,
            double actual, double delta) {
        Assert.assertEquals(message, expected, actual, delta);
    }

    public static void assertEquals(double expected, double actual, double delta) {
        Assert.assertEquals(expected, actual, delta);
    }

    public static void assertEquals(String message, float expected,
            float actual, float delta) {
        Assert.assertEquals(message, expected, actual, delta);
    }

    public static void assertEquals(float expected, float actual, float delta) {
        Assert.assertEquals(expected, actual, delta);
    }

    public static void assertEquals(String message, long expected, long actual) {
        Assert.assertEquals(message, expected, actual);
    }

    public static void assertEquals(long expected, long actual) {
        Assert.assertEquals(expected, actual);
    }

    public static void assertEquals(String message, boolean expected,
            boolean actual) {
        Assert.assertEquals(message, expected, actual);
    }

    public static void assertEquals(boolean expected, boolean actual) {
        Assert.assertEquals(expected, actual);
    }

    public static void assertEquals(String message, byte expected, byte actual) {
        Assert.assertEquals(message, expected, actual);
    }

    public static void assertEquals(byte expected, byte actual) {
        Assert.assertEquals(expected, actual);
    }

    public static void assertEquals(String message, char expected, char actual) {
        Assert.assertEquals(message, expected, actual);
    }

    public static void assertEquals(char expected, char actual) {
        Assert.assertEquals(expected, actual);
    }

    public static void assertEquals(String message, short expected, short actual) {
        Assert.assertEquals(message, expected, actual);
    }

    public static void assertEquals(short expected, short actual) {
        Assert.assertEquals(expected, actual);
    }

    public static void assertEquals(String message, int expected, int actual) {
        Assert.assertEquals(message, expected, actual);
    }

    public static void assertEquals(int expected, int actual) {
        Assert.assertEquals(expected, actual);
    }

    public static void assertNotNull(Object object) {
        Assert.assertNotNull(object);
    }

    public static void assertNotNull(String message, Object object) {
        Assert.assertNotNull(message, object);
    }

    public static void assertNull(Object object) {
        Assert.assertNull(object);
    }

    public static void assertNull(String message, Object object) {
        Assert.assertNull(message, object);
    }

    public static void assertSame(String message, Object expected, Object actual) {
        Assert.assertSame(message, expected, actual);
    }

    public static void assertSame(Object expected, Object actual) {
        Assert.assertSame(expected, actual);
    }

    public static void assertNotSame(String message, Object expected,
            Object actual) {
        Assert.assertNotSame(message, expected, actual);
    }

    public static void assertNotSame(Object expected, Object actual) {
        Assert.assertNotSame(expected, actual);
    }

    public static void failSame(String message) {
        Assert.failSame(message);
    }

    public static void failNotSame(String message, Object expected,
            Object actual) {
        Assert.failNotSame(message, expected, actual);
    }

    public static void failNotEquals(String message, Object expected,
            Object actual) {
        Assert.failNotEquals(message, expected, actual);
    }

    public static String format(String message, Object expected, Object actual) {
        return Assert.format(message, expected, actual);
    }

    //测试前的初始化方法
    protected void setUp() throws Exception {
    }

    //测试后的销毁方法
    protected void tearDown() throws Exception {
    }

    public String toString() {
        return getName() + "(" + getClass().getName() + ")";
    }

    public String getName() {
        return this.fName;
    }

    public void setName(String name) {
        this.fName = name;
    }
}

TestSuite

package junit.framework;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.Vector;
import org.junit.internal.MethodSorter;

public class TestSuite implements Test {
    private String fName;

    public static Test createTest(Class<?> theClass, String name) {
        Constructor<?> constructor;
        try {
            constructor = getTestConstructor(theClass);
        } catch (NoSuchMethodException e) {
            return warning("Class "
                    + theClass.getName()
                    + " has no public constructor TestCase(String name) or TestCase()");
        }
        Object test;
        try {
            // 判断参数类型,创建一个测试用例
            if (constructor.getParameterTypes().length == 0) {
                Object test = constructor.newInstance(new Object[0]);
                if ((test instanceof TestCase)) {
                    ((TestCase) test).setName(name);
                }
            } else {
                test = constructor.newInstance(new Object[] { name });
            }
        } catch (InstantiationException e) {
            return warning("Cannot instantiate test case: " + name + " ("
                    + exceptionToString(e) + ")");
        } catch (InvocationTargetException e) {
            return warning("Exception in constructor: " + name + " ("
                    + exceptionToString(e.getTargetException()) + ")");
        } catch (IllegalAccessException e) {
            return warning("Cannot access test case: " + name + " ("
                    + exceptionToString(e) + ")");
        }
        return (Test) test;
    }

    // 创建一个测试用例
    public static Constructor<?> getTestConstructor(Class<?> theClass)
            throws NoSuchMethodException {
        try {
            return theClass.getConstructor(new Class[] { String.class });
        } catch (NoSuchMethodException e) {
        }
        return theClass.getConstructor(new Class[0]);
    }

    // 当创建失败时,返回一些信息
    public static Test warning(final String message) {
        new TestCase("warning") {
            protected void runTest() {
                fail(message);
            }
        };
    }

    // 将异常转换成字符串输出
    private static String exceptionToString(Throwable e) {
        StringWriter stringWriter = new StringWriter();
        PrintWriter writer = new PrintWriter(stringWriter);
        e.printStackTrace(writer);
        return stringWriter.toString();
    }

    private Vector<Test> fTests = new Vector(10);

    public TestSuite() {
    }

    public TestSuite(Class<?> theClass) {
        addTestsFromTestCase(theClass);
    }

    private void addTestsFromTestCase(Class<?> theClass) {
        this.fName = theClass.getName();
        try {
            getTestConstructor(theClass);
        } catch (NoSuchMethodException e) {
            addTest(warning("Class "
                    + theClass.getName()
                    + " has no public constructor TestCase(String name) or TestCase()"));
            return;
        }
        // 是否是公共方法
        if (!Modifier.isPublic(theClass.getModifiers())) {
            addTest(warning("Class " + theClass.getName() + " is not public"));
            return;
        }
        // 添加需要测试的方法
        Class<?> superClass = theClass;
        List<String> names = new ArrayList();
        while (Test.class.isAssignableFrom(superClass)) {
            for (Method each : MethodSorter.getDeclaredMethods(superClass)) {
                addTestMethod(each, names, theClass);
            }
            superClass = superClass.getSuperclass();
        }
        if (this.fTests.size() == 0) {
            addTest(warning("No tests found in " + theClass.getName()));
        }
    }

    public TestSuite(Class<? extends TestCase> theClass, String name) {
        this(theClass);
        setName(name);
    }

    public TestSuite(String name) {
        setName(name);
    }

    public TestSuite(Class<?>... classes) {
        for (Class<?> each : classes) {
            addTest(testCaseForClass(each));
        }
    }

    private Test testCaseForClass(Class<?> each) {
        if (TestCase.class.isAssignableFrom(each)) {
            return new TestSuite(each.asSubclass(TestCase.class));
        }
        return warning(each.getCanonicalName() + " does not extend TestCase");
    }

    public TestSuite(Class<? extends TestCase>[] classes, String name) {
        this(classes);
        setName(name);
    }

    // 添加一个测试用例到suite
    public void addTest(Test test) {
        this.fTests.add(test);
    }

    // 增加一个suite到suite
    public void addTestSuite(Class<? extends TestCase> testClass) {
        addTest(new TestSuite(testClass));
    }

    // 测试用例的数量
    public int countTestCases() {
        int count = 0;
        for (Test each : this.fTests) {
            count += each.countTestCases();
        }
        return count;
    }

    // 获取suite的名字
    public String getName() {
        return this.fName;
    }

    // 执行测试用例
    public void run(TestResult result) {
        for (Test each : this.fTests) {
            if (result.shouldStop()) {
                break;
            }
            runTest(each, result);
        }
    }

    public void runTest(Test test, TestResult result) {
        test.run(result);
    }

    public void setName(String name) {
        this.fName = name;
    }

    // 根据index返回一个测试用例
    public Test testAt(int index) {
        return (Test) this.fTests.get(index);
    }

    public int testCount() {
        return this.fTests.size();
    }

    public Enumeration<Test> tests() {
        return this.fTests.elements();
    }

    public String toString() {
        if (getName() != null) {
            return getName();
        }
        return super.toString();
    }

    // 增加测试方法
    private void addTestMethod(Method m, List<String> names, Class<?> theClass) {
        String name = m.getName();
        if (names.contains(name)) {
            return;
        }
        if (!isPublicTestMethod(m)) {
            if (isTestMethod(m)) {
                addTest(warning("Test method isn't public: " + m.getName()
                        + "(" + theClass.getCanonicalName() + ")"));
            }
            return;
        }
        names.add(name);
        addTest(createTest(theClass, name));
    }

    // 是否为公共的测试方法
    private boolean isPublicTestMethod(Method m) {
        return (isTestMethod(m)) && (Modifier.isPublic(m.getModifiers()));
    }

    // 是否是测试方法
    private boolean isTestMethod(Method m) {
        return (m.getParameterTypes().length == 0)
                && (m.getName().startsWith("test"))
                && (m.getReturnType().equals(Void.TYPE));
    }
}

11.享元模式(Flyweight)

在前面讲解单例模式时,一个类只有一个唯一的对象,也就是说,不管new多少次,只需要创建这个类的一个对象,如果不采用单例模式,每new一次,就会创建一个对象,这对于系统消耗太大,这时就需要使用享元模式。

哪里会使用到享元模式

在程序开发中,经常会使用到数据库,如果采用单例模式,整个系统只使用一个数据库连接,当用户并发量很大时,系统将会变得非常慢,如果用户每一次请求都创建一个新的连接,每次都会花费0.01s-1s的时间,频繁进行数据库连接操作势必占用很多系统资源,网站的响应速度必定下降,严重的甚至会造成服务器的崩溃。这时就要用到享元模式,数据库连接池就是享元模式的典型应用。

还有比如String s1 = “abc”; String s2 = “abc”; 输出s1==s2为true,也就是说虽然是两个字符串,但引用了相同的地址,这不难理解,因为java会频繁地使用这些基本类型和字符串类型,如果每次赋值都创建一个新的对象,内存的开销极大,所以要用享元模式。

享元模式的实现原理

这里写图片描述

单例模式和享元模式实现数据库连接池

要使用连接池,则必须解决好以下几个问题:

  1. 并发问题:为了使连接管理服务具有最大的连通性,必须考虑多线程环境,即并发问题。java自身提供了并发管理的支持,使用synchronized关键字即可确保线程是同步的

  2. 多数据库服务器和多用户:对于大型的企业级应用,常常需要同时连接不同的数据库。可采用的策略是:设计一个符合单例模式的连接池管理类,在连接池管理类的唯一实例被创建时读取一个资源文件,其中资源文件中存放着多个数据库的连接信息。

  3. 事务处理:可以通过设置Connection的AutoCommit的属性为false,然后显示调用commit或rollback方法来实现。但要高效地进行Connection复用,就必须提供相应的事务支持机制。可采用每一个事务独占一个连接池来实现

  4. 连接池的分配与释放

  5. 连接池的配置和维护:连接池中到底应该放置多少连接,才能使系统性能最佳?可设置最小连接数和最大连接数来控制连接池中的连接。

实现连接池的思想都是相似的,但实现连接池的具体方法多种多样,自己可动手实现一个,如下:

DbConnectionPool

package flyweight;

import java.sql.Connection;
import java.sql.SQLException;

public interface DbConnectionPool {

    // 设定连接池中存放连接的数目
    public void setMaxConns(int numConnections);

    // 设定打开或者关闭连接池
    public void setConnWitch(String onOrOff) throws Exception;

    // 产生连接池
    public void initConnPool() throws SQLException;

    // 从连接池中获取连接
    public Connection getConnection() throws SQLException;

    // 将连接返回给连接池
    public void returnConnection() throws SQLException;

    // 销毁连接池
    public void destroyConnPool() throws SQLException; 
}

GdDbConnectionPool

package flyweight;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

public class GdDbConnectionPool implements DbConnectionPool {
    private String DB_DRIVER = "com.mysql.jdbc.Driver";
    private String DB_User = "root";
    private String DB_PASSWD = "root";
    private String DATASRC_URL = "jdbc:mysql://localhost/myApp";
    static final int defaultMaxConnections = 10;// 默认的连接池的大小
    private List freeConnections;// 存放目前空闲的连接,空闲池
    private Map busyConnections;// 存放目前正在使用的连接,繁忙池
    private int maxConnections;// 设定连接池的大小

    public GdDbConnectionPool(int numConnections) {
        maxConnections = numConnections;
        freeConnections = null;
        busyConnections = null;
    }

    public GdDbConnectionPool() {
        maxConnections = defaultMaxConnections;
        freeConnections = null;
        busyConnections = null;
    }

    // 销毁数据库连接池
    public void destroyConnPool() throws SQLException {
        // 假如还有正在使用的连接
        if (busyConnections != null) {
            Set set = busyConnections.entrySet();
            Iterator iterator = set.iterator();
            // 销毁正在使用的连接
            while (iterator.hasNext()) {
                Map.Entry map = (Map.Entry) iterator.next();
                Connection conn = (Connection) map.getValue();
                conn.close();
            }
            busyConnections = null;
        }
        // 假如还有空闲的连接
        if (freeConnections != null) {
            // 销毁空闲的连接
            for (int i = 0; freeConnections.size() > i; i++) {
                Connection conn = (Connection) freeConnections.get(i);
                conn.close();
            }
            freeConnections = null;
        }
    }

    // 获取数据库连接的操作
    public synchronized Connection getConnection() throws SQLException {
        if (freeConnections == null)
            throw new SQLException("连接池还没有创建");
        try {
            // 表示暂时没有空闲的连接
            if (freeConnections.size() == 0)
                wait();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        // 获取空闲池的连接
        Connection conn = (Connection) freeConnections.get(0);

        freeConnections.remove(0);// 在空闲池里销毁已经获取的连接
        busyConnections.put(Thread.currentThread(), conn);// 将已经获取的连接放在繁忙池里
        return conn;
    }

    // 初始化连接池
    public void initConnPool() throws SQLException {
        try {
            freeConnections = new ArrayList(maxConnections);
            busyConnections = new HashMap(maxConnections);
            // 创建连接,并放在连接池中
            Class.forName(DB_DRIVER);
            // conn = DriverManager.getConnection(DATASRC_URL, DB_User,
            // DB_PASSWD);
            // GdDbConnection con = new GdDbConnection(this);
            for (int i = 0; i < maxConnections; i++) {
                freeConnections.add(DriverManager.getConnection(DATASRC_URL,
                        DB_User, DB_PASSWD));
            }
        } catch (Exception e) {
            freeConnections = null;
            busyConnections = null;
            throw new SQLException(e.toString());
        }
    }

    public synchronized void returnConnection() throws SQLException {
        // 从繁忙池中销毁已经返回的连接
        Connection conn = (Connection) busyConnections.get(Thread
                .currentThread());
        if (conn == null)
            throw new SQLException("没有发现繁忙池中有连接");
        busyConnections.remove(Thread.currentThread());
        // 将已经返回的连接重新放回空闲池中
        freeConnections.add(conn);
        notify();
    }

    // 设置数据库的开关
    public void setConnWitch(String onOrOff) throws Exception {
        try {
            // 假如为ON,则表示初始化连接池
            if ("ON".equalsIgnoreCase(onOrOff)) {
                initConnPool();
                // 假如为OFF,则表示销毁连接池
            } else if ("OFF".equalsIgnoreCase(onOrOff))
                destroyConnPool();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void setMaxConns(int numConnections) {
        maxConnections = numConnections;
    }
}

DbConnectionManager

package flyweight;

import java.sql.Connection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

public class DbConnectionManager {

    static private DbConnectionManager instance; // 单例模式
    private static Map connPool = new HashMap();

    // 返回唯一实例.如果是第一次调用此方法,则创建实例
    static synchronized public DbConnectionManager getInstance() {
        if (instance == null) {
            instance = new DbConnectionManager();
        }
        return instance;
    }

    // 防止其它对象创建本类的实例
    private DbConnectionManager() {
        init();
    }

    // 根据名称从map中获取连接
    public static Connection getConnection(String name) {
        Connection conn = null;
        try {
            // 从连接池中获取连接
            DbConnectionPool pool = (GdDbConnectionPool) connPool.get(name);
            if (pool != null)
                conn = pool.getConnection();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            return conn;
        }
    }

    // 将连接返回给连接池
    public static void returnConnection(String name, Connection conn) {
        try {
            DbConnectionPool pool = (GdDbConnectionPool) connPool.get(name);
            if (pool != null)
                pool.returnConnection();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    // 关闭所有连接
    public synchronized void release() {
        try {
            Set set = connPool.entrySet();
            Iterator iterator = set.iterator();
            while (iterator.hasNext()) {
                Map.Entry map = (Map.Entry) iterator.next();
                DbConnectionPool pool = (GdDbConnectionPool) map.getValue();
                // 关闭连接池
                pool.setConnWitch("OFF");
            }
            connPool = null;
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    // 创建连接池
    private void createPools() {
        DbConnectionPool pool = new GdDbConnectionPool();
        // 设定连接池的大小
        pool.setMaxConns(10);
        try {
            // 表示创建连接池
            pool.setConnWitch("ON");
            // 将创建后的连接池放在Map中,用mysql表示是用于连接mysql数据库的
            connPool.put("mysql", pool);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    // 初始化
    private void init() {
        // 创建连接池
        createPools();
    }
}

享元模式在PROXOOL的应用

hibernate提供了对PROXOOL的支持,ProxoolConnectionProvider.java就是用单例模式实现连接池的具体代码类,可是书上的源码在网上找不到,但思路与上面的自己写的代码想通。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值