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会频繁地使用这些基本类型和字符串类型,如果每次赋值都创建一个新的对象,内存的开销极大,所以要用享元模式。
享元模式的实现原理
单例模式和享元模式实现数据库连接池
要使用连接池,则必须解决好以下几个问题:
并发问题:为了使连接管理服务具有最大的连通性,必须考虑多线程环境,即并发问题。java自身提供了并发管理的支持,使用synchronized关键字即可确保线程是同步的
多数据库服务器和多用户:对于大型的企业级应用,常常需要同时连接不同的数据库。可采用的策略是:设计一个符合单例模式的连接池管理类,在连接池管理类的唯一实例被创建时读取一个资源文件,其中资源文件中存放着多个数据库的连接信息。
事务处理:可以通过设置Connection的AutoCommit的属性为false,然后显示调用commit或rollback方法来实现。但要高效地进行Connection复用,就必须提供相应的事务支持机制。可采用每一个事务独占一个连接池来实现
连接池的分配与释放
连接池的配置和维护:连接池中到底应该放置多少连接,才能使系统性能最佳?可设置最小连接数和最大连接数来控制连接池中的连接。
实现连接池的思想都是相似的,但实现连接池的具体方法多种多样,自己可动手实现一个,如下:
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就是用单例模式实现连接池的具体代码类,可是书上的源码在网上找不到,但思路与上面的自己写的代码想通。