-
Notifications
You must be signed in to change notification settings - Fork 57
Refactor Strategy classes to eliminate duplication using Template Method pattern #109
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Co-authored-by: JusterZhu <11714536+JusterZhu@users.noreply.github.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This pull request successfully refactors the Strategy pattern implementation across GeneralUpdate.Core and GeneralUpdate.ClientCore by introducing a Template Method pattern that eliminates significant code duplication while maintaining all platform-specific functionality.
Changes:
- Created a Template Method pattern in AbstractStrategy with
ExecuteAsync()handling common execution flow - Added hook methods (
CreatePipelineContext,BuildPipeline,OnExecuteComplete,HandleExecuteException) for customization - Reduced duplicate code by 232 lines (41% reduction from 563 to 331 lines)
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| AbstractStrategy.cs | Implements Template Method pattern with common execution flow, context creation, error handling, and utility methods |
| Core/WindowsStrategy.cs | Simplified to override only BuildPipeline, CreatePipelineContext (for driver config), and OnExecuteComplete (to start app) |
| Core/LinuxStrategy.cs | Similar to WindowsStrategy, adds Execute() method that wraps ExecuteAsync, fixed error message to reference LinuxStrategy |
| ClientCore/WindowsStrategy.cs | Overrides only BuildPipeline and CreatePipelineContext (minimal customization) - no OnExecuteComplete since StartApp is called externally |
| ClientCore/LinuxStrategy.cs | Adds blacklist context parameters in CreatePipelineContext, otherwise similar to ClientCore/WindowsStrategy |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| } | ||
| var context = base.CreatePipelineContext(version, patchPath); | ||
|
|
||
| // Add ClientCore-specific context items (blacklists are not needed for Windows in Core) |
Copilot
AI
Jan 19, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The comment incorrectly states "blacklists are not needed for Windows in Core" when it should say "blacklists are not needed for Windows in ClientCore". The word "Core" at the end refers to GeneralUpdate.Core, but this class is in GeneralUpdate.ClientCore.
| // Add ClientCore-specific context items (blacklists are not needed for Windows in Core) | |
| // Add ClientCore-specific context items (blacklists are not needed for Windows in ClientCore) |
| EventManager.Instance.Dispatch(this, new ExceptionEventArgs(e, e.Message)); | ||
| } | ||
| }); | ||
| ExecuteAsync().Wait(); |
Copilot
AI
Jan 19, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using .Wait() on an async method can potentially cause deadlocks in certain synchronization contexts and swallows AggregateException details. Consider using .GetAwaiter().GetResult() instead, which preserves exception details and is less prone to deadlocks.
| ExecuteAsync().Wait(); | |
| ExecuteAsync().GetAwaiter().GetResult(); |
| // Driver middleware (Windows-specific) | ||
| if (_configinfo.DriveEnabled == true) | ||
| { | ||
| var status = ReportType.None; | ||
| var patchPath = StorageManager.GetTempDirectory(Patchs); | ||
| foreach (var version in _configinfo.UpdateVersions) | ||
| { | ||
| try | ||
| { | ||
| var context = new PipelineContext(); | ||
| //Common | ||
| context.Add("ZipFilePath", | ||
| Path.Combine(_configinfo.TempPath, $"{version.Name}{_configinfo.Format}")); | ||
| //Hash middleware | ||
| context.Add("Hash", version.Hash); | ||
| //Zip middleware | ||
| context.Add("Format", _configinfo.Format); | ||
| context.Add("Name", version.Name); | ||
| context.Add("Encoding", _configinfo.Encoding); | ||
| //Patch middleware | ||
| context.Add("SourcePath", _configinfo.InstallPath); | ||
| context.Add("PatchPath", patchPath); | ||
| context.Add("PatchEnabled", _configinfo.PatchEnabled); | ||
| //Driver middleware | ||
| if (_configinfo.DriveEnabled == true) | ||
| { | ||
| context.Add("DriverOutPut", StorageManager.GetTempDirectory("DriverOutPut")); | ||
| context.Add("FieldMappings", _configinfo.FieldMappings); | ||
| } | ||
|
|
||
| var pipelineBuilder = new PipelineBuilder(context) | ||
| .UseMiddlewareIf<PatchMiddleware>(_configinfo.PatchEnabled) | ||
| .UseMiddleware<CompressMiddleware>() | ||
| .UseMiddleware<HashMiddleware>() | ||
| .UseMiddlewareIf<DriverMiddleware>(_configinfo.DriveEnabled); | ||
| await pipelineBuilder.Build(); | ||
| status = ReportType.Success; | ||
| } | ||
| catch (Exception e) | ||
| { | ||
| status = ReportType.Failure; | ||
| GeneralTracer.Error( | ||
| "The Execute method in the GeneralUpdate.Core.WindowsStrategy class throws an exception.", | ||
| e); | ||
| EventManager.Instance.Dispatch(this, new ExceptionEventArgs(e, e.Message)); | ||
| } | ||
| finally | ||
| { | ||
| await VersionService.Report(_configinfo.ReportUrl | ||
| , version.RecordId | ||
| , status | ||
| , version.AppType | ||
| , _configinfo.Scheme | ||
| , _configinfo.Token); | ||
| } | ||
| } | ||
| context.Add("DriverOutPut", StorageManager.GetTempDirectory("DriverOutPut")); | ||
| context.Add("FieldMappings", _configinfo.FieldMappings); | ||
| } |
Copilot
AI
Jan 19, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The Driver middleware configuration logic is identical between WindowsStrategy and LinuxStrategy. Consider moving this logic to the base CreatePipelineContext method in AbstractStrategy to further reduce duplication. The base method could check DriveEnabled and add the driver context parameters, eliminating the need for both strategies to override this.
LinuxStrategy and WindowsStrategy in both GeneralUpdate.Core and GeneralUpdate.ClientCore contained ~80% duplicate code for pipeline execution, context setup, error handling, and version reporting.
Changes
AbstractStrategy Base Class
ExecuteAsync()with common execution flowCreatePipelineContext(version, patchPath)for context customizationBuildPipeline(context)for platform-specific middleware chainsOnExecuteComplete()hook for post-execution logicCheckPath()helper andHandleExecuteException()to base classStrategy Subclasses
Each now only defines platform-specific behavior:
Before (WindowsStrategy example):
After:
Impact
Original prompt
✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.