design-patterns
npx skills add https://github.com/tencentblueking/bk-ci --skill design-patterns
Agent 安装分布
Skill 文档
Skill 27: BK-CI 项ç®è®¾è®¡æ¨¡å¼å®è·µæå
æ¦è¿°
æ¬æåæ»ç»äº BK-CI 项ç®ä¸å¹¿æ³ä½¿ç¨ç设计模å¼åå ¶å®é åºç¨åºæ¯ï¼å¸®å©å¼åè çè§£é¡¹ç®æ¶æå¹¶éµå¾ªç»ä¸ç设计模å¼è§èã
1. 工忍¡å¼ï¼Factory Patternï¼
1.1 ç®åå·¥åï¼Simple Factoryï¼
åºç¨åºæ¯ï¼å建ç¹å®ç±»åç对象ï¼éè¿ç±»ååæ°å³å®å®ä¾ååªä¸ªç±»ã
å®é æ¡ä¾ï¼
TaskFactory – ä»»å¡å·¥å
ä½ç½®ï¼worker-common/src/main/kotlin/com/tencent/devops/worker/common/task/TaskFactory.kt
å®ç°æ¹å¼ï¼
object TaskFactory {
private val taskMap = ConcurrentHashMap<String, KClass<out ITask>>()
fun init() {
// 注åå
置任å¡
register(LinuxScriptElement.classType, LinuxScriptTask::class)
register(WindowsScriptElement.classType, WindowsScriptTask::class)
register(MarketBuildAtomElement.classType, MarketAtomTask::class)
// éè¿åå°æ«æå¹¶æ³¨åæä»¶ä»»å¡
val reflections = Reflections("com.tencent.devops.plugin.worker.task")
val taskClasses = reflections.getSubTypesOf(ITask::class.java)
taskClasses?.forEach { taskClazz ->
val taskClassType = taskClazz.getAnnotation(TaskClassType::class.java)
taskClassType?.classTypes?.forEach { classType ->
register(classType, taskClazz.kotlin)
}
}
}
fun create(type: String): ITask {
val clazz = taskMap[type] ?: return EmptyTask(type)
return clazz.primaryConstructor?.call() ?: EmptyTask(type)
}
}
ä½¿ç¨æ¹å¼ï¼
// æ ¹æ® element ç±»åå建对åºçä»»å¡å®ä¾
val task = TaskFactory.create(element.getClassType())
task.run(buildTask, param, elementId)
ç¹ç¹ï¼
- ä½¿ç¨ Kotlin
objectå®ç°åä¾ - æ¯æåå°æ«æèªå¨æ³¨å
- æ¯æä¼å
级è¦çï¼
priorityåæ®µï¼ - 使ç¨
ConcurrentHashMapä¿è¯çº¿ç¨å®å ¨
ScmFactory – 代ç åºå·¥å
ä½ç½®ï¼common-scm/src/main/kotlin/com/tencent/devops/scm/ScmFactory.kt
å®ç°æ¹å¼ï¼
object ScmFactory {
private val gitApi = GitApi()
private val svnApi = SVNApi()
fun getScm(
projectName: String,
url: String,
type: ScmType, // CODE_SVN, CODE_GIT, CODE_TGIT, CODE_GITLAB, CODE_P4
// ... å
¶ä»åæ°
): IScm {
return when (type) {
ScmType.CODE_SVN -> CodeSvnScmImpl(...)
ScmType.CODE_GIT -> CodeGitScmImpl(...)
ScmType.CODE_TGIT -> CodeTGitScmImpl(...)
ScmType.CODE_GITLAB -> CodeGitlabScmImpl(...)
ScmType.CODE_P4 -> CodeP4ScmImpl(...)
else -> throw TaskExecuteException(
errorCode = ErrorCode.USER_RESOURCE_NOT_FOUND,
errorMsg = "Unknown repo($type)"
)
}
}
}
使ç¨åºæ¯ï¼æ ¹æ®ä»£ç åºç±»åï¼SVNãGitãGitLabãP4ï¼å建对åºç SCM å®ç°ã
å ¶ä»å·¥åå®ä¾
| å·¥åç±» | ä½ç½® | ç¨é |
|---|---|---|
ApiFactory |
worker-common/api/ApiFactory.kt |
å建 Worker 端 API 客æ·ç«¯ |
CommandFactory |
worker-common/task/script/CommandFactory.kt |
åå»ºèæ¬å½ä»¤æ§è¡å¨ |
AtomRunConditionFactory |
worker-common/task/market/AtomRunConditionFactory.kt |
å建æä»¶è¿è¡æ¡ä»¶å¤çå¨ |
PathFilterFactory |
common-webhook/service/code/filter/PathFilterFactory.kt |
å建 Webhook è·¯å¾è¿æ»¤å¨ |
DigestFactory |
common-api/factory/DigestFactory.kt |
å建æè¦ç®æ³å®ç° |
1.2 æ½è±¡å·¥åï¼Abstract Factoryï¼
åºç¨åºæ¯ï¼å建ä¸ç³»åç¸å ³å¯¹è±¡çå·¥åã
å®é æ¡ä¾ï¼
BkApiHandleFactory – API å¤çå¨å·¥å
ä½ç½®ï¼common-web/src/main/kotlin/com/tencent/devops/common/web/factory/BkApiHandleFactory.kt
å®ç°æ¹å¼ï¼
object BkApiHandleFactory {
private val handleMap = ConcurrentHashMap<String, BkApiHandleInterface>()
fun registerHandle(handle: BkApiHandleInterface) {
handleMap[handle.code()] = handle
}
fun getHandle(code: String): BkApiHandleInterface? {
return handleMap[code]
}
}
// 使ç¨ç¤ºä¾ï¼æ ¹æ® API ç±»åè·å对åºçå¤çå¨
interface BkApiHandleInterface {
fun code(): String // è¿å API ç±»åæ è¯
fun handle(request: BkApiRequest): BkApiResponse
}
2. å便¨¡å¼ï¼Singleton Patternï¼
2.1 Kotlin Object åä¾
åºç¨åºæ¯ï¼å ¨å±å¯ä¸å®ä¾ï¼æä¾ç»ä¸è®¿é®ç¹ã
å®ç°æ¹å¼ï¼ä½¿ç¨ Kotlin object å
³é®åã
å®é æ¡ä¾ï¼
// å·¥ååä¾
object TaskFactory { ... }
object ScmFactory { ... }
object ApiFactory { ... }
// å·¥å
·ç±»åä¾
object JsonUtil { ... }
object DateTimeUtil { ... }
ç¹ç¹ï¼
- 线ç¨å®å ¨ï¼ç± JVM ä¿è¯ï¼
- å»¶è¿åå§åï¼é¦æ¬¡è®¿é®æ¶åå§åï¼
- æ æ³è¢«ç»§æ¿
2.2 Spring åä¾
åºç¨åºæ¯ï¼ä¸å¡æå¡ç±»ï¼éè¿ Spring 容å¨ç®¡ççå½å¨æã
å®ç°æ¹å¼ï¼
@Service // é»è®¤åä¾
class PipelineService { ... }
@Component // é»è®¤åä¾
class UserArchivedPipelinePermissionCheckStrategy { ... }
ç¹ç¹ï¼
- Spring 容å¨ç®¡ç
- æ¯æä¾èµæ³¨å ¥
- æ¯æ AOP å¢å¼º
3. 建é è æ¨¡å¼ï¼Builder Patternï¼
3.1 Kotlin Data Class + å½ååæ°
åºç¨åºæ¯ï¼æå»ºå¤æå¯¹è±¡ï¼æä¾æ¸ æ°çæé åæ°ã
å®ç°æ¹å¼ï¼
data class PipelineTriggerEventBuilder(
var projectId: String? = null,
var pipelineId: String? = null,
var userId: String? = null,
var triggerType: String? = null,
var triggerUser: String? = null,
var eventSource: String? = null,
// ... æ´å¤å段
) {
fun build(): PipelineTriggerEvent {
return PipelineTriggerEvent(
projectId = projectId ?: throw IllegalArgumentException("projectId is required"),
pipelineId = pipelineId ?: throw IllegalArgumentException("pipelineId is required"),
// ... å
¶ä»å段
)
}
}
ä½¿ç¨æ¹å¼ï¼
val event = PipelineTriggerEventBuilder()
.apply {
projectId = "demo"
pipelineId = "p-12345"
userId = "admin"
triggerType = "MANUAL"
}
.build()
3.2 Fluent Builderï¼é¾å¼è°ç¨ï¼
å®é æ¡ä¾ï¼
class Ansi(private var builder: StringBuilder) {
fun bold(): Ansi {
builder.append("\u001B[1m")
return this
}
fun fgRed(): Ansi {
builder.append("\u001B[31m")
return this
}
fun reset(): Ansi {
builder.append("\u001B[0m")
return this
}
fun toString(): String = builder.toString()
}
// 使ç¨
val text = Ansi(StringBuilder())
.bold()
.fgRed()
.append("Error: ")
.reset()
.append("Build failed")
.toString()
4. çç¥æ¨¡å¼ï¼Strategy Patternï¼
4.1 æ¥å£ + å®ç°ç±»
åºç¨åºæ¯ï¼æ ¹æ®è¿è¡æ¶æ¡ä»¶éæ©ä¸åçç®æ³å®ç°ã
å®é æ¡ä¾ï¼
æµæ°´çº¿æéæ£æ¥çç¥
ä½ç½®ï¼process/biz-process/src/main/kotlin/com/tencent/devops/process/strategy/
æ¥å£å®ä¹ï¼
interface IUserPipelinePermissionCheckStrategy {
fun checkUserPipelinePermission(
userId: String,
projectId: String,
pipelineId: String,
permission: AuthPermission,
message: String? = null
)
}
å®ç°ç±»ï¼
@Component
class UserNormalPipelinePermissionCheckStrategy : IUserPipelinePermissionCheckStrategy {
override fun checkUserPipelinePermission(...) {
// æ£å¸¸æµæ°´çº¿çæéæ£æ¥é»è¾
}
}
@Component
class UserArchivedPipelinePermissionCheckStrategy : IUserPipelinePermissionCheckStrategy {
override fun checkUserPipelinePermission(...) {
// 彿¡£æµæ°´çº¿çæéæ£æ¥é»è¾
}
}
çç¥å·¥åï¼
object UserPipelinePermissionCheckStrategyFactory {
fun getStrategy(archived: Boolean): IUserPipelinePermissionCheckStrategy {
return if (archived) {
SpringContextUtil.getBean(UserArchivedPipelinePermissionCheckStrategy::class.java)
} else {
SpringContextUtil.getBean(UserNormalPipelinePermissionCheckStrategy::class.java)
}
}
}
ä½¿ç¨æ¹å¼ï¼
val strategy = UserPipelinePermissionCheckStrategyFactory.getStrategy(pipeline.archived)
strategy.checkUserPipelinePermission(userId, projectId, pipelineId, AuthPermission.VIEW)
4.2 æ°æ®è¿ç§»çç¥
ä½ç½®ï¼misc/biz-misc/src/main/kotlin/com/tencent/devops/misc/strategy/
æ¥å£å®ä¹ï¼
interface MigrationStrategy {
fun migrate(projectId: String, pipelineId: String): Boolean
}
å®ç°ç±»ç¤ºä¾ï¼
PipelineInfoMigrationStrategy– æµæ°´çº¿åºæ¬ä¿¡æ¯è¿ç§»PipelineSettingMigrationStrategy– æµæ°´çº¿è®¾ç½®è¿ç§»TemplatePipelineMigrationStrategy– æ¨¡æ¿æµæ°´çº¿è¿ç§»PipelineYamlInfoMigrationStrategy– YAML æµæ°´çº¿è¿ç§»- å ±è®¡ 20+ 个è¿ç§»çç¥å®ç°ç±»
ç¹ç¹ï¼
- æ¯ä¸ªçç¥è´è´£ç¹å®æ°æ®çè¿ç§»
- éè¿ Spring 容å¨ç®¡ç
- æ¯æç»åå¤ä¸ªçç¥æ§è¡
5. è´£ä»»é¾æ¨¡å¼ï¼Chain of Responsibilityï¼
5.1 Handler + HandlerChain
åºç¨åºæ¯ï¼å°è¯·æ±æ²¿çå¤çå¨é¾ä¼ éï¼ç´å°æä¸ªå¤çå¨å¤çå®ã
å®é æ¡ä¾ï¼
ç åååºå建å¤çé¾
ä½ç½®ï¼store/biz-store/src/main/kotlin/com/tencent/devops/store/common/handler/
æ¥å£å®ä¹ï¼
interface Handler<T : HandlerRequest> {
/**
* è½å¦æ»¡è¶³è¿è¡æ¡ä»¶
*/
fun canExecute(handlerRequest: T): Boolean
/**
* æ ¸å¿å¤çé»è¾
*/
fun execute(handlerRequest: T)
/**
* æ§è¡æ»å
¥å£
*/
fun doExecute(handlerRequest: T, chain: HandlerChain<T>) {
if (canExecute(handlerRequest)) {
execute(handlerRequest)
}
chain.handleRequest(handlerRequest) // ä¼ éç»ä¸ä¸ä¸ªå¤çå¨
}
}
interface HandlerChain<T : HandlerRequest> {
fun nextHandler(handlerRequest: T): Handler<T>?
fun handleRequest(handlerRequest: T) {
val handler = nextHandler(handlerRequest)
handler?.doExecute(handlerRequest, this)
}
}
å¤çå¨é¾å®ç°ï¼
class StoreCreateHandlerChain(
private val handlerList: MutableList<Handler<StoreCreateRequest>>
) : HandlerChain<StoreCreateRequest> {
override fun nextHandler(handlerRequest: StoreCreateRequest): Handler<StoreCreateRequest>? {
return handlerList.removeFirstOrNull()
}
}
å ·ä½å¤çå¨ï¼
class StoreCreateParamCheckHandler : Handler<StoreCreateRequest> {
override fun canExecute(handlerRequest: StoreCreateRequest): Boolean = true
override fun execute(handlerRequest: StoreCreateRequest) {
// åæ°æ ¡éªé»è¾
if (handlerRequest.atomCode.isBlank()) {
throw InvalidParamException("Atom code is blank")
}
}
}
class StoreCreatePreBusHandler : Handler<StoreCreateRequest> {
override fun canExecute(handlerRequest: StoreCreateRequest): Boolean = true
override fun execute(handlerRequest: StoreCreateRequest) {
// åç½®ä¸å¡é»è¾
}
}
class StoreCreateDataPersistHandler : Handler<StoreCreateRequest> {
override fun canExecute(handlerRequest: StoreCreateRequest): Boolean = true
override fun execute(handlerRequest: StoreCreateRequest) {
// æ°æ®æä¹
å
}
}
ä½¿ç¨æ¹å¼ï¼
val handlerList = mutableListOf(
StoreCreateParamCheckHandler(),
StoreCreatePreBusHandler(),
StoreCreateDataPersistHandler(),
StoreCreatePostBusHandler()
)
val chain = StoreCreateHandlerChain(handlerList)
chain.handleRequest(storeCreateRequest)
5.2 å ¶ä»è´£ä»»é¾å®ä¾
| å¤çé¾ | ç¨é |
|---|---|
StoreCreateHandlerChain |
ç åååºç»ä»¶å建 |
StoreUpdateHandlerChain |
ç åååºç»ä»¶æ´æ° |
StoreDeleteHandlerChain |
ç åååºç»ä»¶å é¤ |
PipelineInterceptorChain |
æµæ°´çº¿å¯å¨æ¦æªå¨é¾ |
WebhookFilterChain |
Webhook è¿æ»¤å¨é¾ |
6. è£ é¥°å¨æ¨¡å¼ï¼Decorator Patternï¼
6.1 å¼å¸¸è£ 饰å¨
ä½ç½®ï¼worker-common/src/main/kotlin/com/tencent/devops/worker/common/exception/TaskExecuteExceptionDecorator.kt
å®ç°æ¹å¼ï¼
interface ExceptionDecorator<T : Throwable> {
fun decorate(exception: T): TaskExecuteException
}
class DefaultExceptionBase : ExceptionDecorator<Throwable> {
override fun decorate(exception: Throwable): TaskExecuteException {
return TaskExecuteException(
errorMsg = exception.message ?: "Unknown error",
errorType = ErrorType.SYSTEM,
errorCode = ErrorCode.SYSTEM_WORKER_INITIALIZATION_ERROR
)
}
}
class FileNotFoundExceptionD : ExceptionDecorator<FileNotFoundException> {
override fun decorate(exception: FileNotFoundException): TaskExecuteException {
return TaskExecuteException(
errorMsg = "File not found: ${exception.message}",
errorType = ErrorType.USER,
errorCode = ErrorCode.USER_RESOURCE_NOT_FOUND
)
}
}
class RemoteServiceExceptionD : ExceptionDecorator<RemoteServiceException> {
override fun decorate(exception: RemoteServiceException): TaskExecuteException {
return TaskExecuteException(
errorMsg = "Remote service error: ${exception.errorMessage}",
errorType = ErrorType.THIRD_PARTY,
errorCode = exception.errorCode
)
}
}
ä½¿ç¨æ¹å¼ï¼
object TaskExecuteExceptionDecorator {
private val factory = mapOf(
IllegalStateException::class to IllegalStateExceptionD(),
FileNotFoundException::class to FileNotFoundExceptionD(),
RemoteServiceException::class to RemoteServiceExceptionD(),
IOException::class to IOExceptionD()
)
fun decorate(exception: Throwable): TaskExecuteException {
val decorator = factory[exception::class] ?: DefaultExceptionBase()
return decorator.decorate(exception)
}
}
ä½ç¨ï¼å°åç§å¼å¸¸ç»ä¸è£
饰æ TaskExecuteExceptionï¼ä¾¿äºç»ä¸å¤ç忥å¿è®°å½ã
6.2 æéæå¡è£ 饰å¨
ä½ç½®ï¼auth/biz-auth/src/main/kotlin/com/tencent/devops/auth/provider/rbac/service/DelegatingPermissionServiceDecorator.kt
å®ç°æ¹å¼ï¼
class DelegatingPermissionServiceDecorator(
private val delegate: PermissionService,
private val extraCheckers: List<PermissionChecker>
) : PermissionService {
override fun checkPermission(userId: String, resourceType: String, action: String): Boolean {
// å
æ§è¡å§æå¯¹è±¡çæéæ£æ¥
if (!delegate.checkPermission(userId, resourceType, action)) {
return false
}
// åæ§è¡é¢å¤çæ£æ¥å¨
return extraCheckers.all { it.check(userId, resourceType, action) }
}
}
ä½ç¨ï¼å¨åææéæå¡åºç¡ä¸å¢å é¢å¤çæéæ£æ¥é»è¾ï¼èä¸ä¿®æ¹åææå¡ä»£ç ã
7. è§å¯è 模å¼ï¼Observer Patternï¼
7.1 Spring Event å®ç°
åºç¨åºæ¯ï¼äºä»¶é©±å¨æ¶æï¼å®ç°æ¨¡åé´è§£è¦ã
å®é æ¡ä¾ï¼
æµæ°´çº¿äºä»¶çå¬
äºä»¶å®ä¹ï¼
data class ProjectBroadCastEvent(
val projectId: String,
val eventType: EventType,
val userId: String
) : ApplicationEvent(projectId)
äºä»¶çå¬å¨æ¥å£ï¼
interface ProjectEventListener : EventListener<ProjectBroadCastEvent> {
/**
* å¤ç项ç®å¹¿æäºä»¶
*/
override fun execute(event: ProjectBroadCastEvent)
}
å ·ä½çå¬å¨å®ç°ï¼
@Component
class SampleProjectEventListener : ProjectEventListener {
override fun execute(event: ProjectBroadCastEvent) {
logger.info("Received project event: ${event.eventType} for ${event.projectId}")
when (event.eventType) {
EventType.CREATE -> handleProjectCreate(event)
EventType.UPDATE -> handleProjectUpdate(event)
EventType.DELETE -> handleProjectDelete(event)
}
}
}
åå¸äºä»¶ï¼
@Service
class ProjectService(
private val applicationEventPublisher: ApplicationEventPublisher
) {
fun createProject(projectId: String, userId: String) {
// ... å建项ç®é»è¾
// åå¸äºä»¶
applicationEventPublisher.publishEvent(
ProjectBroadCastEvent(
projectId = projectId,
eventType = EventType.CREATE,
userId = userId
)
)
}
}
7.2 å ¶ä»çå¬å¨å®ä¾
| çå¬å¨ | ç¨é | ä½ç½® |
|---|---|---|
PipelineBuildQualityListener |
æµæ°´çº¿æå»ºè´¨éæ£æ¥ | quality/biz-quality/ |
WebhookEventListener |
Webhook äºä»¶å¤ç | process/biz-process/webhook/ |
PipelineTimerBuildListener |
宿¶è§¦åæå»º | process/biz-process/plugin/trigger/timer/ |
PipelineBuildNotifyListener |
æå»ºéç¥ | process/biz-process/notify/ |
PipelineWebSocketListener |
WebSocket æ¶æ¯æ¨é | process/biz-process/websocket/ |
8. æ¨¡æ¿æ¹æ³æ¨¡å¼ï¼Template Method Patternï¼
8.1 æ½è±¡ç±» + é©åæ¹æ³
åºç¨åºæ¯ï¼å®ä¹ç®æ³æ¡æ¶ï¼åç±»å®ç°å ·ä½æ¥éª¤ã
å®é æ¡ä¾ï¼
æµæ°´çº¿çæ¬å建åç½®å¤çå¨
ä½ç½®ï¼process/biz-process/src/main/kotlin/com/tencent/devops/process/service/pipeline/version/processor/
æ¥å£å®ä¹ï¼
interface PipelineVersionCreatePostProcessor {
/**
* çæ¬å建åçåç½®å¤ç
*/
fun postProcessBeforeVersionCreate(
context: PipelineVersionCreateContext,
pipelineModel: Model,
pipelineSetting: PipelineSetting
) {
// é»è®¤ç©ºå®ç°
}
/**
* çæ¬å建åçåç½®å¤ç
*/
fun postProcessAfterVersionCreate(
context: PipelineVersionCreateContext,
pipelineModel: Model,
pipelineSetting: PipelineSetting
) {
// é»è®¤ç©ºå®ç°
}
}
å ·ä½å®ç°ï¼
@Service
class PipelineOperateLogVersionPostProcessor : PipelineVersionCreatePostProcessor {
override fun postProcessAfterVersionCreate(
context: PipelineVersionCreateContext,
pipelineModel: Model,
pipelineSetting: PipelineSetting
) {
// è®°å½æä½æ¥å¿
pipelineOperateLogService.save(
projectId = context.projectId,
pipelineId = context.pipelineId,
versionId = context.versionId,
operateType = "CREATE",
userId = context.userId
)
}
}
@Service
class PipelineEventVersionPostProcessor : PipelineVersionCreatePostProcessor {
override fun postProcessAfterVersionCreate(
context: PipelineVersionCreateContext,
pipelineModel: Model,
pipelineSetting: PipelineSetting
) {
// åéäºä»¶
applicationEventPublisher.publishEvent(
PipelineVersionCreateEvent(
projectId = context.projectId,
pipelineId = context.pipelineId,
versionId = context.versionId
)
)
}
}
å¤çå¨ç¼æï¼
@Service
class PipelineVersionCreateService(
private val postProcessors: List<PipelineVersionCreatePostProcessor>
) {
fun createVersion(context: PipelineVersionCreateContext, model: Model, setting: PipelineSetting) {
// åç½®å¤ç
postProcessors.forEach { it.postProcessBeforeVersionCreate(context, model, setting) }
// æ ¸å¿é»è¾ï¼åå»ºçæ¬
val versionId = doCreateVersion(context, model, setting)
context.versionId = versionId
// åç½®å¤ç
postProcessors.forEach { it.postProcessAfterVersionCreate(context, model, setting) }
}
}
ç¹ç¹ï¼
- ä½¿ç¨æ¥å£èéæ½è±¡ç±»ï¼Kotlin æ¨èï¼
- æä¾é»è®¤ç©ºå®ç°ï¼Java 8+ default methodï¼
- æ¯æå¤ä¸ªåç½®å¤çå¨ç¼æ
- å¤çå¨éè¿ Spring èªå¨æ³¨å ¥
8.2 å ¶ä»åç½®å¤çå¨å®ä¾
| åç½®å¤çå¨ | ç¨é |
|---|---|
PipelineOperateLogVersionPostProcessor |
è®°å½æä½æ¥å¿ |
PipelineEventVersionPostProcessor |
åéçæ¬äºä»¶ |
PipelineModelTaskVersionPostProcessor |
å¤ç模åä»»å¡ |
PipelinePermissionVersionPostProcessor |
å¤çæé |
PipelineTemplateRelationVersionPostProcessor |
å¤ç模æ¿å ³ç³» |
SubPipelineVersionPostProcessor |
å¤çåæµæ°´çº¿ |
PipelineDebugVersionPostProcessor |
å¤çè°è¯æµæ°´çº¿ |
9. éé 卿¨¡å¼ï¼Adapter Patternï¼
9.1 æ¥å£éé
åºç¨åºæ¯ï¼å°ä¸ä¸ªæ¥å£è½¬æ¢æå®¢æ·ææçå¦ä¸ä¸ªæ¥å£ã
å®é æ¡ä¾ï¼
代ç åºæå¡éé å¨
ä½ç½®ï¼repository/biz-repository/src/main/kotlin/com/tencent/devops/repository/service/
ç®æ æ¥å£ï¼
interface IRepositoryService {
fun getRepository(projectId: String, repositoryId: String): Repository
fun listRepositories(projectId: String): List<Repository>
fun createRepository(projectId: String, request: RepositoryCreateRequest): Repository
}
éé å¨å®ç°ï¼
@Service
class CodeGitRepositoryService(
private val gitApi: GitApi,
private val credentialService: CredentialService
) : IRepositoryService {
override fun getRepository(projectId: String, repositoryId: String): Repository {
// è°ç¨ Git API è·åä»åºä¿¡æ¯
val gitRepo = gitApi.getRepository(repositoryId)
// 转æ¢ä¸ºç»ä¸ç Repository 模å
return Repository(
projectId = projectId,
repositoryId = repositoryId,
aliasName = gitRepo.name,
url = gitRepo.url,
type = ScmType.CODE_GIT
)
}
override fun listRepositories(projectId: String): List<Repository> {
val gitRepos = gitApi.listRepositories(projectId)
return gitRepos.map { adaptToRepository(it) }
}
}
@Service
class CodeSvnRepositoryService(
private val svnApi: SVNApi,
private val credentialService: CredentialService
) : IRepositoryService {
override fun getRepository(projectId: String, repositoryId: String): Repository {
// è°ç¨ SVN API è·åä»åºä¿¡æ¯
val svnRepo = svnApi.getRepository(repositoryId)
// 转æ¢ä¸ºç»ä¸ç Repository 模å
return Repository(
projectId = projectId,
repositoryId = repositoryId,
aliasName = svnRepo.name,
url = svnRepo.url,
type = ScmType.CODE_SVN
)
}
}
æå¡æ³¨åï¼
@Component
class CodeRepositoryServiceLoader : BeanPostProcessor {
override fun postProcessAfterInitialization(bean: Any, beanName: String): Any {
if (bean is IRepositoryService) {
CodeRepositoryServiceRegistrar.register(bean)
}
return bean
}
}
object CodeRepositoryServiceRegistrar {
private val services = mutableMapOf<ScmType, IRepositoryService>()
fun register(service: IRepositoryService) {
services[service.getScmType()] = service
}
fun getService(scmType: ScmType): IRepositoryService {
return services[scmType] ?: throw IllegalArgumentException("Unsupported scm type: $scmType")
}
}
ä½¿ç¨æ¹å¼ï¼
val service = CodeRepositoryServiceRegistrar.getService(ScmType.CODE_GIT)
val repo = service.getRepository(projectId, repositoryId)
10. é¨é¢æ¨¡å¼ï¼Facade Patternï¼
10.1 å¤æç³»ç»çç®åæ¥å£
åºç¨åºæ¯ï¼ä¸ºå¤æåç³»ç»æä¾ç»ä¸çé«å±æ¥å£ã
å®é æ¡ä¾ï¼
åæ°é¨é¢æå¡
ä½ç½®ï¼process/biz-process/src/main/kotlin/com/tencent/devops/process/service/ParamFacadeService.kt
å®ç°æ¹å¼ï¼
@Service
class ParamFacadeService(
private val buildVariableService: BuildVariableService,
private val pipelineContextService: PipelineContextService,
private val secretService: SecretService,
private val credentialService: CredentialService
) {
/**
* è·åæå»ºåæ°ï¼é¨é¢æ¹æ³ï¼
*/
fun getBuildParameters(
projectId: String,
pipelineId: String,
buildId: String
): Map<String, String> {
// 1. è·åæµæ°´çº¿ä¸ä¸æåé
val contextVars = pipelineContextService.getAllVariables(projectId, pipelineId, buildId)
// 2. è·åæå»ºåé
val buildVars = buildVariableService.getAllVariable(projectId, pipelineId, buildId)
// 3. è·ååè¯åé
val credentialVars = credentialService.getCredentialVariables(projectId, buildId)
// 4. è·åå¯é¥åé
val secretVars = secretService.getSecretVariables(projectId, buildId)
// 5. åå¹¶ææåéï¼ä¼å
级ï¼secret > credential > build > contextï¼
return contextVars + buildVars + credentialVars + secretVars
}
}
ç¹ç¹ï¼
- éèäºå¤ä¸ªåç³»ç»çå¤æäº¤äº
- æä¾ç®åç»ä¸çæ¥å£
- å é¨å¤çä¼å 级ååå¹¶é»è¾
设计模å¼ä½¿ç¨ç»è®¡
使ç¨é¢çæå
| æå | è®¾è®¡æ¨¡å¼ | ä½¿ç¨æ¬¡æ°ï¼ä¼°ç®ï¼ | å ¸ååºç¨ |
|---|---|---|---|
| 1 | å便¨¡å¼ | 50+ | å·¥åç±»ãå·¥å ·ç±» |
| 2 | 工忍¡å¼ | 40+ | TaskFactory, ScmFactory, ApiFactory |
| 3 | çç¥æ¨¡å¼ | 30+ | æéæ£æ¥ãæ°æ®è¿ç§» |
| 4 | è§å¯è æ¨¡å¼ | 25+ | äºä»¶çå¬å¨ |
| 5 | è´£ä»»é¾æ¨¡å¼ | 10+ | Handler é¾ãInterceptor é¾ |
| 6 | æ¨¡æ¿æ¹æ³æ¨¡å¼ | 15+ | åç½®å¤çå¨ |
| 7 | è£ é¥°å¨æ¨¡å¼ | 5+ | å¼å¸¸è£ 饰å¨ãæéè£ é¥°å¨ |
| 8 | 建é è æ¨¡å¼ | 10+ | å¤æå¯¹è±¡æå»º |
| 9 | éé 卿¨¡å¼ | 5+ | SCM éé å¨ |
| 10 | é¨é¢æ¨¡å¼ | 3+ | ParamFacadeService |
设计模å¼éæ©æå
使¶ä½¿ç¨å·¥å模å¼ï¼
åºæ¯ï¼
- â éè¦æ ¹æ®ç±»ååæ°å建ä¸åç对象
- â 对象å建é»è¾å¤æï¼éè¦éä¸ç®¡ç
- â éè¦æ¯ææ©å±ï¼æä»¶åï¼
示ä¾ï¼
TaskFactory– æ ¹æ®ä»»å¡ç±»åå建任å¡å®ä¾ScmFactory– æ ¹æ®ä»£ç åºç±»åå建 SCM å®ä¾
使¶ä½¿ç¨çç¥æ¨¡å¼ï¼
åºæ¯ï¼
- â æå¤ä¸ªç®æ³å®ç°åä¸ä¸ªæ¥å£
- â è¿è¡æ¶æ ¹æ®æ¡ä»¶éæ©ç®æ³
- â é¿å 大é if-else 忝
示ä¾ï¼
IUserPipelinePermissionCheckStrategy– æ ¹æ®æµæ°´çº¿ç¶æéæ©æéæ£æ¥çç¥MigrationStrategy– ä¸åæ°æ®çè¿ç§»çç¥
使¶ä½¿ç¨è´£ä»»é¾æ¨¡å¼ï¼
åºæ¯ï¼
- â 请æ±éè¦ç»è¿å¤ä¸ªå¤çå¨
- â æ¯ä¸ªå¤çå¨ç¬ç«èè´£
- â å¤ç顺åºå¯é ç½®
示ä¾ï¼
StoreCreateHandlerChain– åæ°æ ¡éª â åç½®ä¸å¡ â æ°æ®æä¹ å â åç½®ä¸å¡PipelineInterceptorChain– æéæ£æ¥ â å¹¶åæ§å¶ â é 颿£æ¥
使¶ä½¿ç¨è§å¯è 模å¼ï¼
åºæ¯ï¼
- â ä¸ä¸ªå¯¹è±¡ç¶ææ¹åéè¦éç¥å ¶ä»å¯¹è±¡
- â å®ç°æ¨¡åé´è§£è¦
- â æ¯æå¨æè®¢é /åæ¶è®¢é
示ä¾ï¼
- æµæ°´çº¿å建äºä»¶ â éç¥æéæ¨¡åãè´¨éæ¨¡åãéç¥æ¨¡å
- æå»ºå®æäºä»¶ â éç¥å¶å彿¡£ãæ¥åçæãé®ä»¶åé
使¶ä½¿ç¨è£ 饰卿¨¡å¼ï¼
åºæ¯ï¼
- â 卿å¢å¼ºå¯¹è±¡åè½
- â ä¸ä¿®æ¹åæä»£ç
- â æ¯æå¤å±è£ 饰
示ä¾ï¼
TaskExecuteExceptionDecorator– å°åç§å¼å¸¸ç»ä¸è£ 饰æä¸å¡å¼å¸¸DelegatingPermissionServiceDecorator– å¨åææéæ£æ¥åºç¡ä¸å¢å é¢å¤æ£æ¥
æä½³å®è·µ
1. ä¼å ä½¿ç¨ Kotlin è¯è¨ç¹æ§
// â é¿å
ï¼Java 飿 ¼çå·¥å
class TaskFactory {
companion object {
private var instance: TaskFactory? = null
fun getInstance(): TaskFactory {
if (instance == null) {
synchronized(this) {
if (instance == null) {
instance = TaskFactory()
}
}
}
return instance!!
}
}
}
// â
æ¨èï¼Kotlin object åä¾
object TaskFactory {
// ...
}
2. å©ç¨ Spring 容å¨ç®¡ç对象
// â é¿å
ï¼æå¨ç®¡çåä¾
object PermissionService {
private val rbacService = RbacPermissionService()
private val v3Service = V3PermissionService()
}
// â
æ¨èï¼Spring ä¾èµæ³¨å
¥
@Service
class PermissionService(
private val rbacService: RbacPermissionService,
private val v3Service: V3PermissionService
)
3. ä½¿ç¨ Data Class + å½ååæ°æ¿ä»£ä¼ ç» Builder
// â é¿å
ï¼å¤æç Builder
class PipelineBuilder {
private var projectId: String? = null
private var pipelineId: String? = null
fun projectId(projectId: String): PipelineBuilder {
this.projectId = projectId
return this
}
fun pipelineId(pipelineId: String): PipelineBuilder {
this.pipelineId = pipelineId
return this
}
fun build(): Pipeline { ... }
}
// â
æ¨èï¼Data Class + å½ååæ°
data class Pipeline(
val projectId: String,
val pipelineId: String,
val name: String = "",
val desc: String = ""
)
// 使ç¨
val pipeline = Pipeline(
projectId = "demo",
pipelineId = "p-123",
name = "My Pipeline"
)
4. çç¥æ¨¡å¼ä¸å·¥å模å¼ç»å
// çç¥æ¥å£
interface IPermissionCheckStrategy {
fun check(userId: String, resourceId: String): Boolean
}
// çç¥å·¥å
object PermissionCheckStrategyFactory {
private val strategies = mapOf(
"RBAC" to RbacPermissionCheckStrategy(),
"V3" to V3PermissionCheckStrategy()
)
fun getStrategy(type: String): IPermissionCheckStrategy {
return strategies[type] ?: throw IllegalArgumentException("Unknown strategy: $type")
}
}
5. è´£ä»»é¾æ¨¡å¼ä½¿ç¨ Spring èªå¨æ³¨å ¥
@Service
class StoreCreateService(
// Spring èªå¨æ³¨å
¥ææ Handler å®ç°
private val handlers: List<Handler<StoreCreateRequest>>
) {
fun create(request: StoreCreateRequest) {
val chain = StoreCreateHandlerChain(handlers.toMutableList())
chain.handleRequest(request)
}
}
ç¸å ³æä»¶ç´¢å¼
工忍¡å¼
worker-common/task/TaskFactory.kt– ä»»å¡å·¥åcommon-scm/ScmFactory.kt– SCM å·¥åworker-common/api/ApiFactory.kt– API å·¥åcommon-api/factory/DigestFactory.kt– æè¦ç®æ³å·¥å
çç¥æ¨¡å¼
process/biz-process/strategy/bus/– æéæ£æ¥çç¥misc/biz-misc/strategy/impl/– æ°æ®è¿ç§»çç¥ï¼20+ 个ï¼log/biz-log/strategy/factory/– æ¥å¿æéæ£æ¥çç¥
è´£ä»»é¾æ¨¡å¼
store/biz-store/common/handler/– ç åååºå¤çé¾process/biz-base/engine/interceptor/– æµæ°´çº¿æ¦æªå¨é¾common-webhook/service/code/filter/– Webhook è¿æ»¤å¨é¾
è§å¯è 模å¼
process/biz-process/engine/listener/– æµæ°´çº¿äºä»¶çå¬å¨quality/biz-quality/listener/– è´¨éæ£æ¥çå¬å¨project/biz-project/listener/– 项ç®äºä»¶çå¬å¨
æ¨¡æ¿æ¹æ³æ¨¡å¼
process/biz-process/service/pipeline/version/processor/– æµæ°´çº¿çæ¬åç½®å¤çå¨process/biz-process/service/template/v2/version/processor/– 模æ¿çæ¬åç½®å¤çå¨
è£ é¥°å¨æ¨¡å¼
worker-common/exception/TaskExecuteExceptionDecorator.kt– å¼å¸¸è£ 饰å¨auth/biz-auth/provider/rbac/service/DelegatingPermissionServiceDecorator.kt– æéè£ é¥°å¨
æ©å±é 读
- utility-components (è´£ä»»é¾æ¨¡å¼) – è´£ä»»é¾æ¨¡å¼è¯¦ç»æå
- microservice-infrastructure (äºä»¶é©±å¨) – äºä»¶é©±å¨æ¶æï¼è§å¯è 模å¼çåºç¨ï¼
- 01-å端微æå¡å¼å – å¾®æå¡å¼åè§è
æ»ç»
BK-CI 项ç®å¹¿æ³ä½¿ç¨äºå¤ç§è®¾è®¡æ¨¡å¼ï¼ä¸»è¦ç¹ç¹ï¼
- 以 Kotlin æ¯ç¨æ³ä¸ºä¸»ï¼ä½¿ç¨
objectåä¾ãæ°æ®ç±»ãå½ååæ°ç - ä¸ Spring 深度éæï¼å©ç¨ä¾èµæ³¨å ¥ãäºä»¶æºå¶
- æ³¨éæ©å±æ§ï¼å·¥å模å¼ãçç¥æ¨¡å¼æ¯ææä»¶å
- 模åè§£è¦ï¼è§å¯è 模å¼ãè´£ä»»é¾æ¨¡å¼å®ç°æ¨¡åé´è§£è¦
- 代ç å¯ç»´æ¤æ§ï¼ç»ä¸ç设计模å¼éä½å¦ä¹ ææ¬
å¨å¼åæ°åè½æ¶ï¼åºåèç°æè®¾è®¡æ¨¡å¼å®ç°ï¼ä¿æä»£ç 飿 ¼ä¸è´æ§ã