microsoft / microsoft/TypeScript
Proposal: delegateof
还没有人认领这个 Issue。
- 主要语言
- Go
- 星标
- 111k
- 派生
- 14.3k
- 平均合并
- 2 天 4 小时
- 30 天内合并 PR
- 132
描述
delegateof Proposal
delegate (noun)
A person sent or authorized to represent others, in particular an elected representative sent to a conference.
Motivation
A popular and widely applied practice for creating flexible and reusable object oriented code is delegation.
Delegation involves two objects handling a request: a receiving object(delegator) delegates operations to its delegate.
An example of this is the Strategy Pattern.
Problem
In order for the delegate pattern to be successful, the delegate must be able to have access to the same context as the delegator.
While the delegator context can be passed to the delegate in the form of raw parameters (delegateOperation(delegateRequiredParam1,delegateRequiredParam2,delegateRequiredParam3)) this approach restricts the amount of responsibility that can be delegated in a nice, scalable way.
To remove such restriction, the delegate should receive a reference to the delegator object and have the possibility of accessing a wider interface than the one of a foreign caller.
Example
Given
class Player {
protected xPos: number;
protected yPos: number;
constructor(private movementStrategy: PlayerMovementStrategy) {
}
protected startAnimation(animationId: string) {
// Code to start an animation
}
protected setMovementStrategy(movementStrategy: PlayerMovementStrategy){
this.movementStrategy = movementStrategy;
}
move(direction: Direction) {
this.movementStrategy.move(this, direction);
}
}
class WalkingMovementStrategy implements PlayerMovementStrategy {
move(player: Player, direction: Direction) {
player.startAnimation('someWalkingAnimation');
switch (direction) {
case Direction.UP:
player.setMovementStrategy(flyingStrategy);
break;
case Direction.Right:
player.xPos = player.xPos + 1;
// Rest of cases
}
}
}
class FlyingMovementStrategy implements PlayerMovementStrategy {
move(player: Player, direction: Direction) {
player.startAnimation('flyingAnimation');
switch (direction) {
case Direction.Up:
player.yPos = player.yPos + 10;
break;
// Rest of cases
}
}
}
interface PlayerMovementStrategy {
move(player: Player, direction: Direction): void;
}
enum Direction {
Up,
Down,
Left,
Right,
}
Then
const coolPlayer = new Player(new WalkingMovementStrategy());
coolPlayer.move(Direction.Up);
Property startAnimation is protected an is only accessible within the class Player.
Things to note
Changing the startAnimation member accessibility to public solves the problem but exposes a method intended for internal usage.
Same goes for the other attributes xPos, yPos.
The delegate must change more than 1 thing of the delegator object, hence making returning a value to apply the changes possible but ugly.
Something like this
const result = movementStrategy.move(player, direction);
console.log(result);
// { nextAnimation: 'walkingAnimation', newYPos : 2 }
And what about extensibility? What happens if a movement strategy wants to trigger multiple animations?
Solution
Add the functionality: objectA delegateof objectB
What does it do?
delegateof allows objectA to have access to the protected members of objectB.
Example
class Player{
constructor(private movementStrategy: PlayerMovementStrategy){}
protected startAnimation(animationId: string){
// start the animation
}
move(direction: Direction){
this.movementStrategy.move(this, direction);
}
}
class WalkingMovementStrategy delegateof Player implements PlayerMovementStrategy{
move(player: Player, direction: Direction){
// No member accesibility errors yay
player.startAnimation('yayAnimation');
}
}
Closing thoughts
While the keyword suffices for the OOP style, I am not sure how could this be implemented for plain objects or if it should even be attempted to.
Example
const playerMovementStrategy = {
move(player: Player, direction: Direction){
// How to make this work here... or do we even need this?
player.startAnimation('yayAnimation');
}
}
Eager to hear your thoughts on it,
Joel.
Edit on member privacy
@andy-ms mentioned privacy as a concern. If we would like to retain the privacy of protected and private members we could introduce a new accessor delegated which functions as protected but allows classes which declare themselves as delegates of the class to be able to access the member.
class Player{
constructor(private movementStrategy: PlayerMovementStrategy){}
delegated startAnimation(animationId: string){
// start the animation
}
move(direction: Direction){
this.movementStrategy.move(this, direction);
}
}
class WalkingMovementStrategy delegateof Player implements PlayerMovementStrategy{
move(player: Player, direction: Direction){
// No member accesibility errors ;-)
player.startAnimation('yayAnimation');
}
}
贡献指南
从这里开始
- 先读完整个 Issue,再读项目的贡献指南。
- 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
- Fork 仓库,在一个分支上完成修改。
- 提交 Pull Request,并在描述里引用这个 Issue 编号。
调研方向
未指定任何仓库文件、测试或编译器入口点。请先审查拟议的 delegateof 语法、其关于受保护成员和隐私的要求,以及有关 plain object 的未解决问题;在语言设计和验收标准达成一致之前,还不具备开始工作的条件。
由索引模型根据 Issue 内容生成。
评估
- 技术栈
- typescript
- 领域
- compilers
- Issue 类型
- 功能
- 难度
- 5/5
- 预计耗时
- 一周以上
- 活跃度
- 停滞
- 描述清晰度
- 需要澄清
- 新手友好度
- 20/100