Unity 6.3
0 онлайн 55 гостей 3 в системе
Вход
Руководство по стилю C# для чистого и масштабируемого кода Глава 7 из 13 Оригинал, стр. 43

Методы

Methods

You know you are working on clean code when each routine you read turns out to be pretty much what you expected. – Ward Cunningham, inventor of Wiki and cofounder of eXtreme Programming

Like classes, methods should be small with a single responsibility. Each method should describe one action or answer one question. It shouldn’t do both. A good name for a method reflects what it does. For example, GetDistanceToTarget is a name that clarifies its intended purpose. Some will argue that being even more explicit with GetDistanceToTargetInMeters to make it clear that the distance is being returned in meters is also preferable, if your project has multiple units of measurement. Try the following suggestions when you create methods for your custom classes: —

Use fewer arguments: Arguments can increase the complexity of your method. Reduce their number to make your methods easier to read and test.

Avoid excessive overloading: You can generate an endless permutation of method overloads. Select the few that reflect how you will call the method and implement those.

If you do overload a method, prevent confusion by making sure each method signature has a distinct number of arguments. Avoid side effects: A method only needs to do what its name advertises. Avoid modifying anything outside of its scope. Pass in arguments by value instead of by reference when possible. If sending back results via the out or ref keyword, make sure that’s the one thing you intend the method to accomplish.

Though side effects are useful for certain tasks, they can lead to unintended consequences. Write a method without side effects to cut down on unexpected behavior. Instead of passing in a flag, make another method: Don’t set up your method to work in two different modes based on a flag. Make two methods with distinct names. For example, don’t make a GetAngle method that returns degrees or radians based on a flag setting. Instead make methods for GetAngleInDegrees and GetAngleInRadians.

While the Boolean flag as an argument seems innocuous, it can lead to tangled implementation or broken single-responsibility.

Methods vs functions? In Unity and C#, we refer to methods rather than functions because methods are functions that are defined within the context of a class or object. Since C# is an objectoriented programming (OOP) language, everything revolves around classes and objects, and methods are the actions that those objects can perform. A function, on the other hand, is a more generic term for a block of code that performs a specific task. In procedural programming languages, functions can exist independently, but in object-oriented languages like C# they are encapsulated within classes and are thus called methods.

Extension methods Extension methods offer a way to add additional functionality to classes that might otherwise be sealed and can be a clean way to extend the UnityEngine API. To create an extension method, make a static method and use the this keyword before the first argument, which will be the type you want to extend. For example, suppose you want to make a method called ResetTransformation to remove any scaling, rotation, or translation from a GameObject.

You can create a static method passing in a Transform for the first argument with the this keyword:

C#
// EXAMPLE: Define an extension method

public static class TransformExtensions {
    public static void ResetTransformation(this Transform transform) {
        transform.position = Vector3.zero;
        transform.localRotation = Quaternion.identity;
        transform.localScale = Vector3.one;
    }
}
Then, when you want to use it, invoke the ResetTransformation method. The ResetOnStart class calls it on the current Transform during Start. // EXAMPLE: Calling the extension method

public class ResetOnStart : MonoBehaviour {
    void Start() { transform.ResetTransformation(); }
}
For organization purposes, define your extension methods in a static class. For example, you create a class called TransformExtensions for methods that extend Transforms, Vector3Extensions for extending Vector3s, and so on. Extension methods can build many useful utilities without the need to create more Monobehaviours. See Unity Learn: Extension Methods to add them to your gamedev bag of tricks.

The DRY principle: Don’t repeat yourself In The Pragmatic Programmer, Andy Hunt and Dave Thomas formulated the DRY principle, or, “don’t repeat yourself.” This oft-spoken mantra in software engineering advises programmers to avoid duplicate or repetitious logic. In doing so, you can ease bug fixing and maintenance costs. If you follow the singleresponsibility principle, you shouldn’t need to change an unrelated piece of code whenever you modify a class or a method. Quashing a logical bug in a DRY program stops it everywhere.

The opposite of DRY is WET (“we enjoy typing” or “write everything twice”). Programming is WET when there are unnecessary repetitions in the code. Imagine there are two ParticleSystems (explosionA and explosionB) and two AudioClips (soundA and soundB). Each ParticleSystem needs to play with its respective sound, which you can achieve with simple methods like this.

C#
// EXAMPLE: WRITE EVERYTHING TWICE

private void PlayExplosionA(Vector3 hitPosition) {
    explosionA.transform.position = hitPosition;
    explosionA.Stop();
    explosionA.Play();
    AudioSource.PlayClipAtPoint(soundA, hitPosition);
}
private void PlayExplosionB(Vector3 hitPosition) {
    explosionB.transform.position = hitPosition;
    explosionB.Stop();
    explosionB.Play();
    AudioSource.PlayClipAtPoint(soundB, hitPosition);
}
Here each method takes a Vector3 position to move the ParticleSystem into place for playback. First, stop the particles (in case they are already playing) and play the simulation. The AudioSource’s static PlayClipAtPoint method then creates a sound effect at the same location. One method is a cut-and-paste version of the other with a little text replacement. Though this works, you need to make a new method – with duplicate logic – every time you want to create an explosion. Instead, refactor it into one PlayFXWithSound method like this: // EXAMPLE: Refactored DRY version

private void PlayFXWithSound(ParticleSystem particle, AudioClip clip, Vector3 hitPosition) {
    particle.transform.position = hitPosition;
    particle.Stop();
    particle.Play();

AudioSource.PlayClipAtPoint(clip, hitPosition); } Add more ParticleSystems and AudioClips and you can continue using this same method to play them in concert. Note that it’s possible to duplicate code without violating the DRY principle. It’s more important that you don’t duplicate logic. Here, we’ve extracted the core functionality into the PlayFXWithSound method. If you need to adjust the logic, you only need to change it in one method rather than in both PlayExplosionA and PlayExplosionB.