整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

commonJava项目开发用的公用基础模块

ommon

项目介绍

Java项目开发用的公用基础模块,包括:Spring容器初始化、配置文件读取工具类、分页对象、Protobuf工具类、反射工具类等

安装教程

  1. 使用maven package打包成jar,引入到项目中;

使用说明

  1. 读取配置文件工具类的使用

SystemConfig继承了spring默认加载配置文件类org.springframework.beans.factory.config.PropertyPlaceholderConfigurer,并对其进行了增强,增加了一些常用的方法,所以只需要将原来配置文件中的PropertyPlaceholderConfigurer换成SystemConfig类即可,如下,在spring配置文件中加入bean定义:

<bean class="com.cnsugar.common.config.SystemConfig">
 <property name="fileEncoding" value="UTF-8" />
 <property name="locations">
 <list>
 <value>classpath:application.properties</value>
 </list>
 </property>
 </bean>

在任何类中只需要用SystemConfig中的方法即可读取application.properties中的所有配置,如:

String str = SystemConfig.getString("key");
int i = SystemConfig.getInt("key");
  1. Spring容器工具类的使用

通过该工具类,可以使普通的java应用程序集成spring来管理项目的各种对象。

在类中可以通过以下两种方式获取Spring容器中的所有bean对象:

UserService userService = AppContext.getBean("userService");
UserService userService = AppContext.getBean(UserService.class);

注意:如果在SpringMVC项目中使用该工具类,需要在listener初始化方法中调用AppContext.initWebApplicationContext()方法来同步SpringMVC容器,否会在日志中看到spring会初始化两次。如新建一个WebContextListener类:

public class WebContextListener extends org.springframework.web.context.ContextLoaderListener {
 @Override
 public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
 return super.initWebApplicationContext(servletContext);
 }
 @Override
 public void contextInitialized(ServletContextEvent event) {
 super.contextInitialized(event);
 initialize(event.getServletContext());
 StringBuilder sb = new StringBuilder();
 sb.append("\r\n======================================================================\r\n");
 sb.append("\r\n 欢迎使用 ").append(SystemConfig.getConfig("app.name")).append("\r\n");
 sb.append("\r\n Version ").append(SystemConfig.getConfig("api.version")).append("\r\n");
 sb.append("\r\n======================================================================\r\n");
 System.out.println(sb.toString());
 }
 /**
 * 系统初始化
 * @param servletContext
 */
 private void initialize(ServletContext servletContext) {
 AppContext.initApplicationContext(WebApplicationContextUtils.getWebApplicationContext(servletContext));
 //初始化配置属性
 servletContext.setAttribute("conf", SystemConfig.getConfigMap());
 }
 @Override
 public void contextDestroyed(ServletContextEvent event) {
 super.contextDestroyed(event);
 }
}

在web.xml中加入:

<listener>
 <listener-class>com.cnsugar.web.listener.WebContextListener</listener-class>
</listener>
  1. 反射工具类的使用

ReflectUtils工具类中的主要方法介绍:

/**
 * 获取类中非final和static的属性(包括父类中的属性),带Field属性和对应的setter和getter方法Method对象
 */
List<BeanField> getBeanFields(Class clazz);
/**
 * 获取类或所有父类中非final和static的属性
 */
List<Field> getFields(Class clazz);
/**
 * 获取类或所有父类中setter方法
 */
List<Method> getSetterMethods(Class clazz);
/**
 * 获取类或所有父类中getter方法
 */
List<Method> getGetterMethods(Class clazz);
/**
 * 直接读取对象属性值, 无视private/protected修饰符, 不经过getter方法
 */
Object getFieldValue(final Object obj, final String fieldName);
/**
 * 直接设置对象属性值, 无视private/protected修饰符, 不经过setter方法
*/
void setFieldValue(Object obj, String fieldName, Object value);
  1. 分页Page对象的使用

一般结合common-jdbc或基于mybatis的PageInterceptor使用,也可直接传一个数据List进去进行分页,toString()方法会生成基于bootstrap的分页操作栏的html代码,可直接在web页面显示。(后面会详细说明)。

  1. 其他类说明

CamelCaseUtils: 驼峰命名法(CamelCase)和下划线风格(UnderScoreCase)字符串之间的转换工具类;

ProtostuffUtil: protostuff工具类;

Utils: 包含常用的获取系统时间、日期转换、日期比较、时间格式化、普通java类型转换等方法工具类;

Bytes: 各种java类型与byte[]互转工具类;

Base64: Base64编码和解码工具类;

大有学问#

今天实际操作上手有一点小变更,就是将top中的nav-bottom模块重划分到main部分中。

这样刚好是一个完整的main,不用重新切割测量大小。

今天写一个大概的行模块布局,详细部分下次再写。

CSS公共属性

/* 清除浏览器默认设置 */
* {
    margin: 0;
    padding: 0;
}
/* 背景颜色 */
body {
    background-color: #f3f6f6;
}
.bGW {
    background-color: white;
}
/* 添加浮动 */
.fL {
    float: left;
}
.fR {
    float: right;
}
/* 清除浮动 */
.clearF {
    overflow: hidden;
}
/* 版心宽度1200px,水平居中 */
.bX {
    width: 1200px;
    margin: auto;
}

top部分

.top {
    height: 101px;
  <!-- 临时背景颜色,方便看清楚盒子位置,填充实际内容时会删除掉该属性 -->
    background-color: antiquewhite;
}
<div class="top bX"></div>

top部分

nav部分

.nav {
    height: 420px;
  <!-- 这是吸取的真实背景颜色 -->
    background-color: #1c056c;
}
<div class="nav"></div>

nav

main部分

.mB42 {
    margin-bottom: 42px;
}
.menu {
    height: 60px;
    /* 盒子阴影 */
    box-shadow: 1px 2px rgba(118, 118, 118, 0.2);
    margin-top: 8px;
    margin-bottom: 37px;
}
/* 初阶段用于显示模块位置,实际布局需删除该选择器 */
.main-top,
.main-center div,
.main-bottom div {
    background-color:  white;
}
.main-top {
    height: 603px;
}
.main-center {
    height: 923px;
}
.main-center-top,
.main-center-bottom {
    height: 440px;
}
.main-bottom-top,
.main-bottom-bottom {
    height: 318px;
}
<div class="main bX">
  <!-- main可划分为四个大的行模块 -->
  <!-- menu行模块 -->
  <div class="menu bGW">main-menu</div>
  <!-- main-top行模块 -->
  <div class="main-top mB42">
    精品推荐
  </div>
<!-- main-center行模块 -->
  <div class="main-center mB42">
   <div class="main-center-top mB42">
     编程入门
   </div>
   <div class="main-center-bottom mB42">
     数据分析师
   </div>
  </div>
<!-- main-bottom行模块 -->
<div class="main-bottom mB42">
  <div class="main-bottom-top mB42">
    机器学习工程师
  </div>
  <div class="main-bottom-bottom">
    前端开发工程师
  </div>
 </div>
</div>

menu行模块和main-top行模块

main-center行模块

main-bottom行模块

footer部分

.footer {
    height: 417px;
}
<div class="footer bGW">
  <div class="bX">
    footer
  </div>
</div>

footer部分

下次更新完整代码。拜拜

文地址:Understanding Design Patterns in JavaScript

原文作者:Sukhjinder Arora

译者:HelloGitHub-Robert

当启动一个新的项目时候,我们不应该马上开始编程。而是首先应该定义项目的目的和范围,然后列出其功能或规格。如果你已经开始编程或者正在从事一个复杂的项目,则应该选择一个最适合你项目的设计模式。

什么是设计模式?

在软件工程中,设计模式是针对软件设计中常见问题的可重用解决方案。设计模式也是经验丰富的开发人员针对特定问题的最佳实践。它可以被当作编程的模板。

为什么要使用设计模式?

许多工程师要么认为设计模式浪费时间,要么不知道如何恰当的使用设计模式。但如果能正确使用设计模式,则可以帮助你写出更好的可读性更高的代码,并且代码更容易被维护和理解。

最重要的是,设计模式为软件开发人员提供了通用的词汇表。它们能让学习你代码的人很快了解代码的意图。例如,如果你的项目中使用了装饰器模式,那么新的开发可以很快就知道这段代码的作用,从而他们可以将更多精力放在解决业务问题上,而不是试图理解代码在做什么。

我们已经知道了什么是设计模式和它的重要性,下面我们深入研究一下 JavaScript 中的 7 种设计模式。

一、模块模式

模块是一段独立的代码,因此我们可以更新模块而不会影响代码的其它部分。模块还允许我们通过为变量创建单独的作用域来避免命名空间污染。当它们与其它代码解耦时,我们还可以在其它项目中重用模块。

模块是任何现代 JavaScript 应用程序不可或缺的一部分,有助于保持代码干净,独立和有条理。在 JavaScript 中有许多方法可以创建模块,其中一种是模块模式。

与其它编程语言不同,JavaScript 没有访问修饰符,也就是说,你不能将变量声明为私有的或公共的。因此,模块模式也可用来模拟封装的概念。

模块模式使用 IIFE(立即调用的函数表达式),闭包和函数作用域来模拟封装的概念。例如:

const myModule = (function() {  
  const privateVariable = 'Hello World';  
  function privateMethod() {
    console.log(privateVariable);
  }
  return {
    publicMethod: function() {
      privateMethod();
    }
  }
})();
myModule.publicMethod();

由于是 IIFE 因此代码会被立即执行,并将返回对象赋值给了 myModule 变量。由于闭包,即使在 IIFE 完成后,返回的对象仍可以访问 IIFE 内部定义的函数和变量。

因此,IIFE 内部定义的变量和函数对外部是看不见的,从而使其成为 myModule 模块的私有成员。

执行代码后,myModule 变量看起来像下面所示:

const myModule = {
  publicMethod: function() {
    privateMethod();
  }};

因此当我们调用 publicMethod() 时候,它将调用 privateMethod() 例如:

// Prints 'Hello World'
module.publicMethod();

二、揭示模块模式

揭示模块模式是 Christian Heilmann 对模块模式的略微改进。模块模式的问题在于,我们必须创建新的公共函数才能调用私有函数和变量。

在这种模式下,我们将返回的对象的属性映射到要公开暴露的私有函数上。这就是为什么将其称为揭示模块模式。例如:

const myRevealingModule = (function() {  
  let privateVar = 'Peter';
  const publicVar  = 'Hello World';
  function privateFunction() {
    console.log('Name: '+ privateVar);
  }
  
  function publicSetName(name) {
    privateVar = name;
  }
  function publicGetName() {
    privateFunction();
  }
  /** reveal methods and variables by assigning them to object     properties */
return {
    setName: publicSetName,
    greeting: publicVar,
    getName: publicGetName
  };
})();
myRevealingModule.setName('Mark');
// prints Name: Mark
myRevealingModule.getName();

这种模式让我们更容易知道哪些函数和变量是公共的,无形中提高了代码的可读性。执行代码后 myRevealingModule 看起来像下所示:

const myRevealingModule = {
  setName: publicSetName,
  greeting: publicVar,
  getName: publicGetName
};

当我们调用 myRevealingModule.setName('Mark') 时,实际调用了内部的 publicSetName。当调用 myRevealingModule.getName() 时,实际调用了内部的 publicGetName 例如:

myRevealingModule.setName('Mark');
// prints Name: Mark
myRevealingModule.getName();

与模块模式相比,揭示模块模式的优势有:

  • 通过修改 return 语句中的一行,我们可以将成员从公共变为为私人,反之亦然。
  • 返回的对象不包含任何函数定义,所有右侧表达式都在 IIFE 中定义,从而使代码清晰易读。

三、ES6 模块

在 ES6 之前,JavaScript 没有内置模块,因此开发人员必须依靠第三方库或模块模式来实现模块。但是自从 ES6,JavaScript 内置了模块。

ES6 的模块是以文件形式存储的。每个文件只能有一个模块。默认情况下,模块内的所有内容都是私有的。通过使用 export 关键字来暴露函数、变量和类。模块内的代码始终在严格模式下运行。

3.1 导出模块

有两种方法可以导出函数和变量声明:

  • 在函数和变量声明的前面添加 export 关键字。例如:
// utils.js
export const greeting = 'Hello World';
export function sum(num1, num2) {
  console.log('Sum:', num1, num2);
  return num1 + num2;
}
export function subtract(num1, num2) {
  console.log('Subtract:', num1, num2);
  return num1 - num2;
}
// This is a private function
function privateLog() {
  console.log('Private Function');
}
  • 在代码的最后添加 export 关键字来暴露函数和变量。例如:
// utils.js
function multiply(num1, num2) {
  console.log('Multiply:', num1, num2);
  return num1 * num2;
}
function divide(num1, num2) {
  console.log('Divide:', num1, num2);
  return num1 / num2;
}
// This is a private function
function privateLog() {
  console.log('Private Function');
}
export {multiply, divide};

3.2 导入模块

与导出模块相似,有两种使用 import 关键字导入模块的方法。例如:

  • 一次导入多个项目
// main.js
// importing multiple items
import { sum, multiply } from './utils.js';
console.log(sum(3, 7));
console.log(multiply(3, 7));
  • 导入所有模块
// main.js
// importing all of module
import * as utils from './utils.js';
console.log(utils.sum(3, 7));
console.log(utils.multiply(3, 7));

3.3 导入导出中使用别名

  • 重命名导出
// utils.js
function sum(num1, num2) {
  console.log('Sum:', num1, num2);
  return num1 + num2;
}
function multiply(num1, num2) {
  console.log('Multiply:', num1, num2);
  return num1 * num2;
}
export {sum as add, multiply};
  • 重命名导入
// main.js
import { add, multiply as mult } from './utils.js';
console.log(add(3, 7));
console.log(mult(3, 7));

四、单例模式

一个单例对象是只能实例化一次的对象。如果不存在,则单例模式将创建类的新实例。如果存在实例,则仅返回对该对象的引用。重复调用构造函数将始终获取同一对象。

JavaScript 是一直内置单例的语言。我们只是不称它们为单例,我们称它们为对象字面量。例如:

const user = {
  name: 'Peter',
  age: 25,
  job: 'Teacher',
  greet: function() {
    console.log('Hello!');
  }
};

因为 JavaScript 中的每个对象都占用一个唯一的内存位置,并且当我们调用该 user 对象时,实际上是在返回该对象的引用。

如果我们尝试将 user 变量复制到另一个变量并修改该变量。例如:

const user1 = user;
user1.name = 'Mark';

我们将看到两个对象都被修改,因为 JavaScript 中的对象是通过引用而不是通过值传递的。因此,内存中只有一个对象。例如:

// prints 'Mark'
console.log(user.name);
// prints 'Mark'
console.log(user1.name);
// prints true
console.log(user === user1);

可以使用构造函数来实现单例模式。例如:

let instance = null;

function User() {
  if(instance) {
    return instance;
  }
  instance = this;
  this.name = 'Peter';
  this.age = 25;
  
  return instance;
}
const user1 = new User();
const user2 = new User();
// prints true
console.log(user1 === user2);

调用此构造函数时,它将检查 instance 对象是否存在。如果对象不存在,则将 this 变量分配给 instance 变量。如果该对象存在,则只返回该对象。

单例也可以使用模块模式来实现。例如:

const singleton = (function() {
  let instance;
  
  function init() {
    return {
      name: 'Peter',
      age: 24,
    };
  }
  return {
    getInstance: function() {
      if(!instance) {
        instance = init();
      }
      
      return instance;
    }
  }
})();
const instanceA = singleton.getInstance();
const instanceB = singleton.getInstance();
// prints true
console.log(instanceA === instanceB);

在上面的代码中,我们通过调用 singleton.getInstance 方法来创建一个新实例。如果实例已经存在,则此方法仅返回该实例。如果该实例不存在,则通过调用该 init() 函数创建一个新实例。

五、工厂模式

工厂模式使用工厂方法创建对象而不需要指定具体的类或构造函数的模式。

工厂模式用于创建对象而不需要暴露实例化的逻辑。当我们需要根据特定条件生成不同的对象时,可以使用此模式。例如:

class Car{
  constructor(options) {
    this.doors = options.doors || 4;
    this.state = options.state || 'brand new';
    this.color = options.color || 'white';
  }
}
class Truck {
  constructor(options) {
    this.doors = options.doors || 4;
    this.state = options.state || 'used';
    this.color = options.color || 'black';
  }
}
class VehicleFactory {
  createVehicle(options) {
    if(options.vehicleType === 'car') {
      return new Car(options);
    } else if(options.vehicleType === 'truck') {
      return new Truck(options);
      }
  }
}

这里,创建了一个 Car 和一个 Truck 类(具有一些默认值),该类用于创建新的 cartruck对象。而且定义了一个VehicleFactory 类,用来根据 options 对象中的 vehicleType 属性来创建和返回新的对象。

const factory = new VehicleFactory();
const car = factory.createVehicle({
  vehicleType: 'car',
  doors: 4,
  color: 'silver',
  state: 'Brand New'
});
const truck= factory.createVehicle({
  vehicleType: 'truck',
  doors: 2,
  color: 'white',
  state: 'used'
});
// Prints Car {doors: 4, state: "Brand New", color: "silver"}
console.log(car);
// Prints Truck {doors: 2, state: "used", color: "white"}
console.log(truck);

我为类 VehicleFactory 创建了一个新的 factory 对象。然后,我们通过调用 factory.createVehicle 方法并且传递 options 对象,其 vehicleType 属性可能为 car 或者 truck 来创建新 CarTruck 对象。

六、装饰器模式

装饰器模式用于扩展对象的功能,而无需修改现有的类或构造函数。此模式可用于将特征添加到对象中,而无需修改底层的代码。

此模式的一个简单示例为:

function Car(name) {
  this.name = name;
  // Default values
  this.color = 'White';
}
// Creating a new Object to decorate
const tesla= new Car('Tesla Model 3');
// Decorating the object with new functionality
tesla.setColor = function(color) {
  this.color = color;
}
tesla.setPrice = function(price) {
  this.price = price;
}
tesla.setColor('black');
tesla.setPrice(49000);
// prints black
console.log(tesla.color);

这种模式的一个更实际的例子是:

假设汽车的成本取决于其功能的数量。如果没有装饰器模式,我们将不得不为不同的功能组合创建不同的类,每个类都有一个 cost 方法来计算成本。例如:

class Car() {
}

class CarWithAC() {
}

class CarWithAutoTransmission {
}

class CarWithPowerLocks {
}

class CarWithACandPowerLocks {
}

但是,通过装饰器模式,我们可以创建一个基类 car 并且通过装饰器函数给不同的对象添加对应的成本逻辑。

class Car {
  constructor() {
  // Default Cost
  this.cost = function() {
  return 20000;
  }
}
}
// Decorator function
function carWithAC(car) {
  car.hasAC = true;
  const prevCost = car.cost();
  car.cost = function() {
    return prevCost + 500;
  }
}
// Decorator function
function carWithAutoTransmission(car) {
  car.hasAutoTransmission = true;
   const prevCost = car.cost();
  car.cost = function() {
    return prevCost + 2000;
  }
}
// Decorator function
function carWithPowerLocks(car) {
  car.hasPowerLocks = true;
  const prevCost = car.cost();
  car.cost = function() {
    return prevCost + 500;
  }
}

首先,我们创建了小轿车的基类 Car。然后针对要添加的特性创建了装饰器并且此装饰器以 Car 对象为参数。然后通过返回更新后的小汽车成本来覆盖对象的成本函数,且添加了一个用来标识某个特性是否已经被添加的属性。

要添加新的功能,我们只需要像下面一样就可以:

const car = new Car();
console.log(car.cost());
carWithAC(car);
carWithAutoTransmission(car);
carWithPowerLocks(car);

最后,我们可以像这样计算汽车的成本:

// Calculating total cost of the car
console.log(car.cost());

结论

我们已经了解了 JavaScript 中使用的各种设计模式,但是这里没有涉及到可以用 JavaScript 实现的设计模式。

尽管了解各种设计模式很重要,但不要过度使用它们也同样重要。在使用设计模式之前,你应该仔细考虑你的问题是否适合该设计模式。要知道某个模式是否适合你的问题,应该好好研究该设计模式以及它的应用。

最后,欢迎优秀的你加入 HelloGitHub 的「译文亦舞」系列,让你的才华舞动起来!把优秀的文章分享给更多的人。要求:

  • 平时浏览 GitHub、开源、编程、程序员等英文资讯和文章
  • 想把自己阅读到优秀的英文文章分享给更多的人
  • 翻译准确但不是直翻或机翻
  • 保证每月至少翻译或校正 1 篇高质量文章
  • 了解 Markdown 和排版规则
  • 联系我

关注 HelloGitHub 头条号第一时间收到推送