ActionScript3.0面向对象编程基础解析
立即解锁
发布时间: 2025-08-20 00:32:02 阅读量: 1 订阅数: 3 


ActionScript 3.0 设计模式精髓
### ActionScript 3.0 面向对象编程基础解析
#### 1. 面向对象编程概述
面向对象编程(OOP)是一种通过模块化设计实现软件架构灵活性的实践。采用 OOP 的程序员并非一定是更高级的编码者,而是更具策略性的编码者,他们遵循 OOP 的原则。OOP 不是一种语言,而是一种架构实践和思维方式,使得应用程序和语言具有面向对象的特性,如 ActionScript(AS)3.0。
AS 3.0 是为了反映程序员将代码拆分为一系列可相互通信对象的思维模型而构建的面向对象语言。然而,许多使用 AS 3.0 进行开发的人并未采用 OOP,这是因为 OOP 具有一定的挑战性,且学习需要时间。AS 3.0 旨在支持灵活架构的开发,使用 OOP 有助于避免代码变得难以管理。灵活的架构更易于修改,因为构成应用程序的对象具有明确的边界,这使得在处理对象时更容易进行替换。
与之相对的是过程式编程,这是一种线性的开发方法,通常会导致代码行之间缺乏行为分离和思维连贯性。语言仅仅变成了一系列的例程和子例程。如果您是项目的唯一开发者,过程式编程可能效果良好,因为您熟悉自己的代码。但当有更多程序员参与时,他们很难熟悉彼此的代码,并在代码行中查找需要更改的位置。而在 OOP 中,应用程序中的每个行为都包含在一个独特的类中,提供了一种更优雅的方式来查看对象之间的协作。由于每个独特的类都有一个名称,因此很容易追踪;并且由于它应该只具有单一行为,所以该类只有一个更改的理由。
以下是一个将图像转换为半色调图像的过程式代码示例:
```actionscript
import flash.display.BitmapData;
import flash.display.Sprite;
import flash.display.StageAlign;
import flash.geom.ColorTransform;
import flash.geom.Rectangle;
var img : BitmapData = new Buttercup( 1 , 1 );
var sampleSize : int = 4;
var brushSize : int = 4;
var pixelsTall : uint = img.height;
var pixelsWide : uint = img.width;
var rect : Rectangle = new Rectangle( 0 , 0 , sampleSize , sampleSize );
var totalBytesToScan : int = pixelsWide * pixelsTall;
var position : int = 0;
var offset : Number = sampleSize * .5;
var averageColor : uint;
var pixels : Vector.<uint > ;
var darks : Number;
var halftone : Shape = new Shape();
var scale : Number;
while ( position <= totalBytesToScan )
{
pixels = img.getVector( rect );
averageColor = grayScaleAverage( pixels );
darks = brightness( averageColor );
if ( darks > 0 )
{
halftone.graphics.beginFill( averageColor , 1 );
scale = (255 - darks) / 255;
halftone.graphics.drawCircle( rect.x + offset , rect.y + offset , scale * brushSize );
}
if (rect.x >= pixelsWide)
{
rect.x = 0;
rect.y += sampleSize;
}
else
{
rect.x += sampleSize;
}
position += sampleSize * sampleSize;
}
addChild( halftone );
function brightness( color : uint ) : int
{
var R : uint = color >> 16 & 0xff;
var G : uint = color >> 8 & 0xff;
var B : uint = color & 0xff;
return int( 0.2126 * R + 0.7152 * G + 0.0722 * B );
}
function rgbAverage( pixels : Vector.<uint> ) : uint
{
var colors : uint;
var pixelLength : int = pixels.length;
var averageR : uint = 0;
var averageG : uint = 0;
var averageB : uint = 0;
var localPixels : Vector.<uint > = pixels;
while ( --pixelLength >= 0 )
{
color = uint( localPixels[pixelLength] );
averageR += color >> 16 & 0xFF;
averageG += color >> 8 & 0xFF;
averageB += color & 0xFF;
}
averageR /= pixels.length;
averageG /= pixels.length;
averageB /= pixels.length;
color = averageR << 16 | averageG << 8 | averageB;
return color;
}
function grayScaleAverage( pixels : Vector.<uint> ) : uint
{
var color : uint;
var pixelLength : int = pixels.length;
var averageR : uint;
var averageG : uint;
var averageB : uint;
var localPixels : Vector.<uint > = pixels;
while ( --pixelLength >= 0 )
{
color = uint( localPixels[pixelLength] );
averageR += color >> 16 & 0xFF;
averageG += color >> 8 & 0xFF;
averageB += color & 0xFF;
}
averageR /= pixels.length;
averageG /= pixels.length;
averageB /= pixels.length;
var luma : int = ( averageR * 0.3 + averageG * 0.59 + averageB * 0.11 );
color = luma << 16 | luma << 8 | luma;
return color;
}
```
这段代码虽然只有 87 行,但可能会变得难以管理。为了让代码更具组织性和灵活性,可以运用 OOP 的四个原则:封装、多态、继承和数据隐藏。
#### 2. 封装
封装的概念可以通过汽车的例子来理解。当您驾驶汽车时,您知道引擎在引擎盖下,但您忽略这些机械细节,专注于驾驶车辆时需要与之交互的部分。实际上,您关心的是如何舒适地从 A 点到达 B 点,这就是所谓的问题域。在这个例子中,需要关注的是如何保持舒适或导航方向。如果您试图理解引擎,但它们与您的职业或爱好无关,您可能会将注意力转移到一个新的问题域:您抛锚的引擎以及如何修复它。
在半色调应用程序中,应用程序的目标是获取图像并对其色调进行数字更改,以显示半色调效果。就像汽车的例子一样,如果将引擎与其他部分分开,以揭示使应用程序运行的实际代码,那么可以关注以下代码:
```actionscript
import flash.display.BitmapData;
import flash.display.Sprite;
import flash.display.StageAlign;
import flash.geom.ColorTransform;
import flash.geom.Rectangle;
var img : BitmapData = new Buttercup( 1 , 1 );
var sampleSize : int = 4;
var brushSize : int = 4;
var pixelsTall : uint = img.height;
var pixelsWide : uint = img.width;
var rect : Rectangle = new Rectangle( 0 , 0 , sampleSize , sampleSize );
var totalBytesToScan : int = pixelsWide * pixelsTall;
var position : int = 0;
var offset : Number = sampleSize * .5;
var averageColor : uint;
var pixels : Vector.<uint > ;
var darks : Number;
var halftone : Shape = new Shape();
var scale : Number;
while ( position <= totalBytesToScan )
{
pixels = img.getVector( rect );
averageColor = grayScaleAverage( pixels );
darks = brightness( averageColor );
if ( darks > 0 )
{
halftone.graphics.beginFill( averageColor , 1 );
scale = (255 - darks) / 255;
halftone.graphics.drawCircle( rect.x + offset , rect.y + offset , scale * brushSize );
}
if (rect.x >= pixelsWide)
{
rect.x = 0;
rect.y += sampleSize;
}
else
{
rect.x += sampleSize;
}
position += sampleSize * sampleSize;
}
addChild( halftone );
```
通过这种方式,将 87 行代码减少到 40 行来定义系统,这些代码就像是引擎盖下的引擎。移除的代码行执行引擎将使用的行为,应该与上述代码分开,以便开始创建可区分的角色。
#### 3. 多态
多态允许在系统的角色之间定义边界,从而实现具有相似方法参数、方法名称和返回类型的其他行为之间的互换性。方法的这三个组成部分是正确消息传递所需的契约,它们共同被称为签名。只要不同实现之间的签名保持相同,就可以交换行为以实现各种结果,而无需修改太多代码。
考虑从前面代码中提取的两个行为:`grayscaleAverage` 和 `rgbAverage`。这两个行为负责确定参数化像素向量的平均亮度,并返回计算值。返回的值是具有三个颜色通道还是一个颜色通道,取决于执行计算的方法。
由于这两个行为具有不同的方法名称,行为的调用者必须知道正在调用的是哪个行为,这降低了消息传递者和接收者之间的灵活性。为了使这两个行为可以无差别地互换,必须确保两个方法都暴露一个公共接口。由于两个方法的签名和返回类型完全相同,因此需要设计一个公共名称来调用该方法。这两个方法都用于计算给定像素样本的平均亮度,因此可以将 `average` 作为算法之间的公共链接。
然而,仅靠公共接口不足以在过程式编程中实现代码替换。虽然两个方法使用相同的名称,但如果将它们添加到应用程序中进行编译,会抛出错误。因此,需要一种方法来区分这两种实现,同时仍然使用公共方法名称,这就引入了继承的概念。
#### 4. 继承
继承是面向对象语言中的一个重要概念,它允许开发者以层次关系的形式编写代码。作为 OOP 的推动者,程序员可以将一系列行为和属性封装到一个称为对象的独立实体中。然后,可以通过从原始对象派生新对象来创建其他对象。就像孩子可以从父母那里受益一样,对象也可以通过继承受益。子对象使用封装的属性和行为,从而在两者之间创建层次结构。在对象层次结构中,子对象称为子类,其父对象称为超类。
在面向对象语言中,任何子类都可以被泛化为其任何祖先的特定属性和行为集合。将所有封装的行为和属性称为对象,意味着所有关系的层次结构是一个称为 `Object` 的封装。这种在不同实现之间的泛化是实现多态行为所必需的。
为了使用多态行为,引用必须被类型化为泛化类型,以确保分配给该引用的所有对象都具有相似的接口。这样,对象之间的替换不会破坏客户端和接收者之间的消息传递。
为了创建一个通用类型,确保灰度和彩色半色调行为都暴露 `average` 接口,需要创建一个层次关系。在这个例子中,这两个行为是兄弟关系,它们从一个公共超类继承 `average` 接口。继承不仅建立了一个层次结构,使应用程序能够使用多态,还实现了代码重用,从而减少了重复代码。
以下是两种半色调算法的并排比较:
| 彩色半色调行为 | 灰度半色调行为 |
| --- | --- |
| ```actionscript<br>function average(pixels:Vector.<uint>) : uint{<br>var color : uint;<br>var pixelLength : int = pixels.length;<br>var averageR : uint;<br>var averageG : uint;<br>var averageB : uint;<br>var localPixels : Vector.<uint > = pixels;<br><br>while ( --pixelLength >= 0 )<br>{<br>color = uint( localPixels[pixelLength] );<br>averageR += color >> 16 & 0xFF;<br>averageG += color >> 8 & 0xFF;<br>averageB += color & 0xFF;<br>}<br><br>averageR /= pixels.length;<br>averageG /= pixels.length;<br>averageB /= pixels.length;<br>color= averageR << 16 | ```actionscript<br>function average(pixels:Vector.<uint>) :uint{<br>var color : uint;<br>var pixelLength : int = pixels.length;<br>var averageR : uint;<br>var averageG : uint;<br>var averageB : uint;<br>var localPixels : Vector.<uint > = pixels;<br><br>while ( --pixelLength >= 0 )<br>{<br>color = uint( localPixels[pixelLength] );<br>averageR += color >> 16 & 0xFF;<br>averageG += color >> 8 & 0xFF;<br>averageB += color & 0xFF;<br>}<br><br>averageR /= pixels.length;<br>averageG /= pixels.length;<br>averageB /= pixels.length;<br>var luma : int = ( averageR * 0.3 + averageG * 0.59 + averageB * 0.11 );<br><br>color = luma << 16 |
可以看到,这两种实现几乎相同,唯一的区别是灰度实现中 `luma` 变量的声明和计算。通过将两个方法中共同的变量提取出来,并将它们作为超类的属性插入,可以减少代码量并保持局部焦点。
以下是将共同变量提取到超类后的代码示例:
```actionscript
//generic attributes
var color : uint;
var pixelLength : int = pixels.length;
var averageR : uint;
var averageG : uint;
var averageB : uint;
var localPixels : Vector.<uint > = pixels;
//generic operation
function average( pixels : Vector.<uint> ) : uint{
//do nothing
}
//color halftone behavior
function average(pixels:Vector.<uint>):uint{
pixelLength = pixels.length;
localPixels = pixels;
while ( --pixelLength >= 0 )
{
color = uint( localPixels[pixelLength] );
averageR += color >> 16 & 0xFF;
averageG += color >> 8 & 0xFF;
averageB += color & 0xFF;
}
averageR /= pixels.length;
averageG /= pixels.length;
averageB /= pixels.length;
color= averageR << 16 | averageG << 8 | averageB;
return color;
}
//grayscale halftone behavior
function average(pixels:Vector.<uint>):uint{
pixelLength = pixels.length;
localPixels = pixels;
while ( --pixelLength >= 0 )
{
color = uint( localPixels[pixelLength] );
averageR += color >> 16 & 0xFF;
averageG += color >> 8 & 0xFF;
averageB += color & 0xFF;
}
averageR /= pixels.length;
averageG /= pixels.length;
averageB /= pixels.length;
var luma : int = ( averageR * 0.3 + averageG * 0.59 + averageB * 0.11 );
color = luma << 16 | luma << 8 | luma;
return color;
}
```
通过这种方式,可以将两个半色调算法中的通道平均操作移到超类中,作为 `average` 接口的默认实现,这样两个算法都可以使用它。
```actionscript
//ChannelAveraging algorithm
//generic attributes
var color : uint;
var pixelLength : int = pixels.length;
var averageR : uint;
var averageG : uint;
var averageB : uint;
var localPixels : Vector.<uint> = pixels;
//default operation
function average( pixels : Vector.<uint> ) : uint{
while ( --pixelLength >= 0 )
{
color = uint( localPixels[pixelLength] );
averageR += color >> 16 & 0xFF;
averageG += color >> 8 & 0xFF;
averageB += color & 0xFF;
}
averageR /= pixels.length;
averageG /= pixels.length;
averageB /= pixels.length;
return null;
}
//color halftone algorithm
function average(pixels:Vector.<uint>):uint{
super.average(pixels)
color= averageR << 16 | averageG << 8 | averageB;
}
//grayscale halftone algorithm
function average(pixels:Vector.<uint>):uint{
super.average(pixels)
var luma : int = ( averageR * 0.3 + averageG * 0.59 + averageB * 0.11 );
}
```
通过继承,不仅实现了代码的重用,还为应用程序引入了多态性,使得代码更加灵活和易于维护。
### ActionScript 3.0 面向对象编程基础解析
#### 5. 数据隐藏
数据隐藏是面向对象编程中的另一个重要原则,它可以保护对象的内部状态不被外部随意访问和修改,从而提高代码的安全性和可维护性。在前面的半色调应用程序示例中,我们可以通过将一些变量和方法设置为私有,来实现数据隐藏。
例如,在超类中,可以将一些通用的变量设置为私有属性,只允许通过公共方法来访问和修改这些属性。这样,外部代码就无法直接访问和修改这些变量,从而避免了意外的修改和错误。
以下是一个简单的数据隐藏示例:
```actionscript
class HalftoneSuperClass {
private var _color : uint;
private var _pixelLength : int;
private var _averageR : uint;
private var _averageG : uint;
private var _averageB : uint;
private var _localPixels : Vector.<uint>;
public function HalftoneSuperClass(pixels:Vector.<uint>) {
_localPixels = pixels;
_pixelLength = pixels.length;
}
public function getColor() : uint {
return _color;
}
public function setColor(value:uint) : void {
_color = value;
}
public function average( pixels : Vector.<uint> ) : uint{
while ( --_pixelLength >= 0 )
{
_color = uint( _localPixels[_pixelLength] );
_averageR += _color >> 16 & 0xFF;
_averageG += _color >> 8 & 0xFF;
_averageB += _color & 0xFF;
}
_averageR /= pixels.length;
_averageG /= pixels.length;
_averageB /= pixels.length;
return null;
}
}
```
在这个示例中,`_color`、`_pixelLength`、`_averageR`、`_averageG` 和 `_averageB` 都是私有属性,外部代码无法直接访问它们。通过公共的 `getColor()` 和 `setColor()` 方法,可以安全地访问和修改 `_color` 属性。
#### 6. ActionScript 3.0 的特性
ActionScript 3.0 具有许多特性,这些特性有助于更好地实现面向对象编程。以下是一些重要的特性:
- **The Traits Object**:`Traits` 对象提供了关于类、实例和方法的元数据信息。可以使用 `Traits` 对象来检查类的属性、方法和其他信息。
- **Garbage Collection**:垃圾回收是一种自动内存管理机制,它会自动回收不再使用的内存。在 ActionScript 3.0 中,垃圾回收器会定期检查不再被引用的对象,并释放它们占用的内存。
- **Memory Management**:除了垃圾回收,还可以通过手动管理内存来优化性能。例如,可以使用 `Disposable` 模式来确保对象在不再使用时及时释放资源。
- **Mark and Sweep**:标记清除是一种常见的垃圾回收算法。在标记阶段,垃圾回收器会标记所有仍然被引用的对象;在清除阶段,会清除所有未被标记的对象。
- **Strong Typing**:强类型是指变量在声明时必须指定类型。强类型可以提高代码的可读性和可维护性,同时也可以在编译时发现一些类型错误。
- **Runtime Type Checking**:运行时类型检查可以在运行时检查对象的类型。可以使用 `is` 和 `as` 运算符来进行运行时类型检查。
- **Compile-Time Type Checking**:编译时类型检查可以在编译时检查类型错误。强类型和静态类型语言通常会进行编译时类型检查。
#### 7. ActionScript 3.0 的应用
ActionScript 3.0 可以用于开发各种类型的应用程序,包括富互联网应用(RIA)、游戏、动画等。以下是一个简单的 ActionScript 3.0 应用示例:
```actionscript
import flash.display.Sprite;
class Main extends Sprite {
public function Main() {
var circle:Sprite = new Sprite();
circle.graphics.beginFill(0xFF0000);
circle.graphics.drawCircle(50, 50, 25);
circle.graphics.endFill();
addChild(circle);
}
}
```
这个示例创建了一个红色的圆形,并将其添加到舞台上。
#### 8. 总结
通过本文的介绍,我们了解了面向对象编程的四个重要原则:封装、多态、继承和数据隐藏。这些原则可以帮助我们编写更具组织性、灵活性和可维护性的代码。同时,我们还介绍了 ActionScript 3.0 的一些特性和应用,包括垃圾回收、内存管理、强类型等。
在实际开发中,建议遵循面向对象编程的原则,合理使用 ActionScript 3.0 的特性,以提高代码的质量和性能。以下是一个总结表格:
| 原则/特性 | 描述 |
| --- | --- |
| 封装 | 将相关的行为和数据封装在一起,隐藏,提高安全性和可维护性 |
| 多态 | 实现具有相似签名的行为之间的互换性,提高代码的灵活性 |
| 继承 | 通过层次关系实现代码重用和泛化,支持多态行为 |
| 数据隐藏 | 保护对象的内部状态,避免外部随意访问和修改 |
| The Traits Object | 提供类、实例和方法的元数据信息 |
| Garbage Collection | 自动回收不再使用的内存 |
| Memory Management | 手动管理内存以优化性能 |
| Mark and Sweep | 常见的垃圾回收算法 |
| Strong Typing | 变量声明时指定类型,提高可读性和可维护性 |
| Runtime Type Checking | 在运行时检查对象的类型 |
| Compile-Time Type Checking | 在编译时检查类型错误 |
通过合理运用这些原则和特性,可以开发出高质量的 ActionScript 3.0 应用程序。
下面是一个简单的面向对象编程流程的 mermaid 流程图:
```mermaid
graph TD;
A[定义问题] --> B[设计类和对象];
B --> C[实现封装];
C --> D[实现多态];
D --> E[实现继承];
E --> F[实现数据隐藏];
F --> G[开发应用程序];
G --> H[测试和优化];
```
这个流程图展示了一个典型的面向对象编程流程,从定义问题开始,经过设计类和对象、实现各种原则,最后开发出应用程序并进行测试和优化。
0
0
复制全文
相关推荐










