背景
ロジックのいくつかの共通の大部分と混合し、多くのビジネスコード、;結果は、あなたが同様の機能を必要とするとき、1、またはコードブロックのほぼ同じコピーを再書き込みしなければならなかった、につながる可能性があるように、システム障害こするCengceng上昇します。
後で同様のことを行うように密接にだけ、この現象は、ロジックのこれらの一般的な引き出されたチャンクを観察し、再利用可能なマイクロコンポーネントを行います良い抽象的思考を開発者に決定少し仕事ができ支払います。
どのように、そして、一度だけ行われるもののようなものを作り、事業からのコードのマイクロ再利用可能なコンポーネントから引き上げ、将来的にそれを繰り返し再利用することができますか?実施例は、本明細書で説明します。
事業開発では、多くの場合、我々は、IDに応じ名に対応する数だけ存在する必要があります。たとえば、従業員ID番号は、従業員の名前、その上のカテゴリIDに応じたカテゴリの数で見つかった、と名前に基づいて発見しました。ビューの物語の観点から、あなたは、この類似性は、それから引き出されるか、その後、それらの類似性を感じることができますか?
予備コード
あなたがカテゴリIDの数に基づいて、適切なカテゴリの名前を取得したいとします。ほとんどの開発者は、ビジネスニーズを満たすためのコードを書くことができます。
@Component("newCategoryCache")
public class NewCategoryCache {
private static Logger logger = LoggerFactory.getLogger(NewCategoryCache.class);
/**
* 类目ID与名称映射关系的缓存
* 假设每个类目信息 50B , 总共 50000 个类目,
* 那么总占用空间 2500000B = 2.38MB 不会造成影响
*/
private Map<Long, String> categoryCache = new ConcurrentHashMap<>();
@Resource
private CategoryBackService categoryBackService;
@Resource
private MultiTaskExecutor multiTaskExecutor;
public Map<Long, String> getCategoryMap(List<Long> categoryIds) {
List<Long> undupCategoryIds = ListUtil.removeDuplicate(categoryIds);
List<Long> unCached = new ArrayList<>();
Map<Long,String> resultMap = new HashMap<>();
for (Long categoryId: undupCategoryIds) {
String categoryName = categoryCache.get(categoryId);
if (StringUtils.isNotBlank(categoryName)) {
resultMap.put(categoryId, categoryName);
}
else {
unCached.add(categoryId);
}
}
if (CollectionUtils.isEmpty(unCached)) {
return resultMap;
}
Map<Long,String> uncacheCategoryMap = getCategoryMapFromGoods(unCached);
categoryCache.putAll(uncacheCategoryMap);
logger.info("add new categoryMap: {}", uncacheCategoryMap);
resultMap.putAll(uncacheCategoryMap);
return resultMap;
}
private Map<Long,String> getCategoryMapFromGoods(List<Long> categoryIds) {
List<CategoryBackModel> categoryBackModels = multiTaskExecutor.exec(categoryIds,
subCategoryIds -> getCategoryInfo(subCategoryIds), 30);
return StreamUtil.listToMap(categoryBackModels, CategoryBackModel::getId, CategoryBackModel::getName);
}
private List<CategoryBackModel> getCategoryInfo(List<Long> categoryIds) {
CategoryBackParam categoryBackParam = new CategoryBackParam();
categoryBackParam.setIds(categoryIds);
ListResult<CategoryBackModel> categoryResult = categoryBackService.findCategoryList(categoryBackParam);
logger.info("categoryId: {}, categoryResult:{}", categoryIds, JSON.toJSONString(categoryResult));
if (categoryResult == null || !categoryResult.isSuccess()) {
logger.warn("failed to fetch category: categoryIds={}", categoryIds);
return new ArrayList<>();
}
return categoryResult.getData();
}
}
注意すべき点が2つあります。
- バルククエリインタフェースは、パラメータの数照会するすべてのIDを取得するために連続的にまたは同時に、そう分割する、IDSを通過上の限界があるのでCategoryBackService.findCategoryList。
- なぜキーは単なる文字列、オーバーヘッドオブジェクト作成のないたくさんあるので、キャッシュがあり、グアバのキャッシュを行い、複数のスレッドが同時に読み取りまたは書き込みキャッシュが存在しますので、これは、スレッドセーフなローカルキャッシュを使用しています。
多重変換
コードは、典型的には、試料とバッファマイクロアセンブリの操作の混合物です。あなたは従業員IDと従業員名のマッピングが必要な場合は、それより上の部分をコピーする必要があり、その後、別のクラスを書きました。だから、繰り返し作業の多くがあるでしょうが、また、それ以外の守備は、変数の命名とビジネスの意味で見つけるだろう、慎重に事業変数の名前を置き換えるために、編集することがされていない必要があります。あなたが知っています。
それから引き出されたキャッシュ・ウィジェットの一部になる方法はありませんか?これを行うために、我々は同様にこれらの二つのゾーンを分離し、鋭いビジネス感覚と共通する要素を持っている必要があります。
セマンティック分離
まず、サービスの意味論と一般的な技術、論理的に分離された成分から。
この例では、我々はに関連するビジネスセクション、を見て起動することができます。
- カテゴリには、ID、name属性とgetterメソッドを備え、CategoryBackModelオブジェクト。
- categoryBackService.findCategoryList:カテゴリの数がメソッドをオブジェクトを取得します。
その他のキャッシュはロジックを関連付けられています。
第二に、ビジネスのより多くの部分または汎用部品とよりを見てください。マルチサービスの一部ならば、他の一般的なクラスにポンピング部分を入れて、複数の一般的な部分は、業務の一部が別のクラスをポンプで入れた場合。
この例では、NewCategoryCacheキャッシュ部分は、実際には、唯一のビジネスサービスの呼び出しに依存している、大多数を占めました。したがって、ビジネスの一部を抽出することが可能です。
GMが引き出さ
ロータステンプレートメソッドは、GMの部品とサービスの分離の一部です。
次に、上記getCategoryInfoトラフィック部分は、コールバックとして渡さサブクラス、基底クラスに置かれるべきです。この方法は、まず、適切に行うには、この依存性を表現しGETLISTに抽象化することができ、それは、IDのリストに従ってオブジェクトのリストを得ることです:
protected abstract List<Domain> getList(List<Long> ids);
ここで、ドメインID、名前の方法、したがって、ドメインへのインターフェースとしてそこに定義されなければなりません。
public interface Domain {
Long getId();
String getName();
}
このように、getCategoryMapFromGoodsは形式で書くことができ、唯一の特定のビジネスコールの独立したインターフェース、独自の定義に依存します。
private Map<Long,String> getMapFromService(List<Long> ids) {
List<Domain> models = multiTaskExecutor.exec(ids,
subIds -> getList(subIds), 30);
return StreamUtil.listToMap(models, Domain::getId, Domain::getName);
}
名前(カテゴリ)のすべての部分は、ビジネス上の意味を外しているNewCategoryCacheその後、それは次のようになります。
public abstract class AbstractCache {
private static Logger logger = LoggerFactory.getLogger(AbstractCache.class);
@Resource
protected MultiTaskExecutor multiTaskExecutor;
public Map<Long, String> getMap(List<Long> ids) {
List<Long> undupIds = ListUtil.removeDuplicate(ids);
List<Long> unCached = new ArrayList<>();
Map<Long,String> resultMap = new HashMap<>();
for (Long id: undupIds) {
String name = getCache().get(id);
if (StringUtils.isNotBlank(name)) {
resultMap.put(id, name);
}
else {
unCached.add(id);
}
}
if (CollectionUtils.isEmpty(unCached)) {
return resultMap;
}
Map<Long,String> uncacheMap = getMapFromService(unCached);
getCache().putAll(uncacheMap);
logger.info("add new cacheMap: {}", uncacheMap);
resultMap.putAll(uncacheMap);
return resultMap;
}
private Map<Long,String> getMapFromService(List<Long> ids) {
List<Domain> models = multiTaskExecutor.exec(ids,
subIds -> getList(subIds), 30);
return StreamUtil.listToMap(models, Domain::getId, Domain::getName);
}
protected abstract List<Domain> getList(List<Long> ids);
protected abstract ConcurrentMap<Long,String> getCache();
public interface Domain {
Long getId();
String getName();
}
}
AbstractCacheこのクラスは、もはや任意のビジネスセマンティクスを持っていません。
注意:キャッシュは、通常、混合することがされていない別の企業であるため、その理由は、getCache()抽象メソッドを引き抜きます。キーは、ビジネスの名前空間の値が付いている場合はもちろん、それが唯一のキャッシュである、グローバルな一貫性があります。
ビジネスデタッチ
次に、新しいサービスは、クラスの一部にすることができます。
@Component("newCategoryCacheV2")
public class NewCategoryCacheV2 extends AbstractCache {
private static Logger logger = LoggerFactory.getLogger(NewCategoryCacheV2.class);
/**
* 类目ID与名称映射关系的缓存
* 假设每个类目信息 50B , 总共 50000 个类目,
* 那么总占用空间 2500000B = 2.38MB 不会造成影响
*/
private ConcurrentMap<Long, String> categoryCache = new ConcurrentHashMap<>();
@Resource
private CategoryBackService categoryBackService;
public Map<Long,String> getCategoryMap(List<Long> categoryIds) {
return getMap(categoryIds);
}
@Override
public List<Domain> getList(List<Long> ids) {
CategoryBackParam categoryBackParam = new CategoryBackParam();
categoryBackParam.setIds(ids);
ListResult<CategoryBackModel> categoryResult = categoryBackService.findCategoryList(categoryBackParam);
logger.info("categoryId: {}, categoryResult:{}", ids, JSON.toJSONString(categoryResult));
if (categoryResult == null || !categoryResult.isSuccess()) {
logger.warn("failed to fetch category: categoryIds={}", ids);
return new ArrayList<>();
}
return categoryResult.getData().stream().map( categoryBackModel -> new Domain() {
@Override
public Long getId() {
return categoryBackModel.getId();
}
@Override
public String getName() {
return categoryBackModel.getName();
}
}).collect(Collectors.toList());
}
@Override
protected ConcurrentMap<Long, String> getCache() {
return categoryCache;
}
}
このように、あなたは完了です。一品作られた感はありますか?
ビジネスセマンティクスを強調するために、newCategoryCacheV2はGetMapリクエスト適応パッケージを提供し、外部サービスの一貫性を確保することを言及する価値があります。
単一の時間
シングル測定は非常に重要です。ここでは参考のために、newCategoryCacheV2単一のプローブ上に掲載:
class NewCategoryCacheV2Test extends Specification {
NewCategoryCacheV2 newCategoryCache = new NewCategoryCacheV2()
CategoryBackService categoryBackService = Mock(CategoryBackService)
MultiTaskExecutor multiTaskExecutor = new MultiTaskExecutor()
def setup() {
Map<Long, String> categoryCache = new ConcurrentHashMap<>()
categoryCache.put(3188L, "qin")
categoryCache.put(3125L, 'qun')
newCategoryCache.categoryCache = categoryCache
newCategoryCache.categoryBackService = categoryBackService
ExportThreadPoolExecutor exportThreadPoolExecutor = ExportThreadPoolExecutor.getInstance(5,5,1L,1, "export")
multiTaskExecutor.generalThreadPoolExecutor = exportThreadPoolExecutor
newCategoryCache.multiTaskExecutor = multiTaskExecutor
}
@Test
def "tesGetCategoryMap"() {
given:
def categoryList = [
new CategoryBackModel(id: 1122L, name: '衣服'),
new CategoryBackModel(id: 2233L, name: '食品')
]
categoryBackService.findCategoryList(_) >> [
code: 200,
message: 'success',
success: true,
data: categoryList,
count: 2
]
categoryList
when:
def categoryIds = [3188L, 3125L, 3125L, 3188L, 1122L, 2233L]
def categoryMap = newCategoryCache.getCategoryMap(categoryIds)
then:
categoryMap[3188L] == 'qin'
categoryMap[3125L] == 'qun'
categoryMap[1122L] == '衣服'
categoryMap[2233L] == '食品'
}
}
概要
マイクロコンポーネントは、再利用可能なビジネスコードから切り離さどのようなもののソートが一度だけ実行されるように、本明細書に記載される一例では、、、将来的には、繰り返し再利用することができます。思考とスキルのこの種の設計能力を強化するために、継続的な研修を通じて向上させることができる非常に便利です。