• No results found

INF 19 Interfaces

N/A
N/A
Protected

Academic year: 2020

Share "INF 19 Interfaces"

Copied!
36
0
0

Loading.... (view fulltext now)

Full text

(1)

Interfaces

Interfaces

Subjects:

-

Interface definition

-

Interface features

-

Implementing an interface

-

Obtaining interface references

-

Interfaces as return values

-

Resolving name clashes via explicit interface implementation

-

Designing interface hierarchies

-

Building enumerable types

Subjects:

-

Interface definition

-

Interface features

-

Implementing an interface

-

Obtaining interface references

-

Interfaces as return values

-

Resolving name clashes via explicit interface implementation

-

Designing interface hierarchies

(2)

Introduction

Introduction

A class can contain a reference to another class; this statement describes the simple

HAS_A relationship. One class can extend another class by way of the marvel of inheritance — that’s the IS_A relationship. The C# interface implements another, equally important association: the CAN_BE_USED_AS relationship.

Introducing CAN_BE_USED_AS

If you want to jot a note, you can scribble it with a pen, type it into your smartphone, or pound it out on your laptop’s keyboard. You can fairly say that all three objects — pen, smartphone, and computer — implement the TakeANote operation. Suppose that you use the magic of inheritance to implement this concept in C#:

abstract class ThingsThatRecord // The base class {

abstract public void TakeANote(string note); }

public class Pen : ThingsThatRecord // A subclass {

override public void TakeANote(string note) {

(3)

Introducing CAN_BE_USED_AS

Introducing CAN_BE_USED_AS

}

public class PDA : ThingsThatRecord // Another subclass {

override public void TakeANote(string note) {

// ... stroke a note on the PDA ... }

}

public class LapTop : ThingsThatRecord // A third subclass {

override public void TakeANote(string note) {

// ... tap, tap, tap ... }

}

The following simple method shows the inheritance approach working just fine:

void RecordTask(ThingsThatRecord recorder) // Parameter type is base class.

{

// All classes that extend ThingsThatRecord have a TakeANote method. recorder.TakeANote(“Shopping list”);

(4)

Introducing CAN_BE_USED_AS

Introducing CAN_BE_USED_AS

The parameter type is ThingsThatRecord, so you can pass any subclasses to this method, making the method quite general.

That might seem like a good solution, but it has two big drawbacks:

A fundamental problem: Do Pen, PDA, and LapTop truly have an IS_A relationship? Are those three items all the same type in real life? All I can say is that ThingsThatRecord makes a poor base class here.

A purely technical problem: You might reasonably derive both LapTop and PDA as subclasses of Computer. But nobody would say that a Pen IS_A Computer. You have to characterize a pen as a type of MechanicalWritingDevice or DeviceThatStainsYourShirt. But a C# class can’t inherit from two different base classes at the same time — a C# class can be only one type of item.

So the Pen, PDA, and LapTop classes have in common only the characteristic that they

(5)

Abstract classes

Abstract classes

Abstract classes are used to describe abstract concepts. An abstract class is a class with one or more abstract methods. An abstract method is a method marked abstract. An abstract method has no implementation.

Abstract classes are closely related to interfaces. They are classes that cannot be instantiated, and are frequently either partially implemented, or not at all implemented.

One key difference between abstract classes and interfaces is that a class may implement an unlimited number of interfaces, but may inherit from only one abstract (or any other kind of) class.

(6)

Knowing What an Interface Is

Knowing What an Interface Is

An interface in C# resembles a class with no data members and nothing but abstract methods, almost like an abstract class — almost:

interface IRecordable {

void TakeANote(string note); }

The interface begins with the interface keyword. It contains nothing but abstract methods. It has no data members and no implemented methods. Interfaces can contain a few other features, including properties, events, and indexers.

Among the elements that a C# interface cannot exhibit are ✦ Access specifiers, such as public or private;

✦ Keywords such as virtual, override, or abstract; ✦ Data members;

✦ Implemented methods — nonabstract methods with bodies.

(7)

How to implement an interface

How to implement an interface

To put a C# interface to use, you implement it with one or more classes. The class heading looks like this:

class Pen : IRecordable // Looks like inheritance, but isn’t

A C# interface specifies that classes which implement the interface must provide specific implementations. They must. For example, any class that implements the IRecordable interface must provide an implementation for the TakeANote method. The method that implements TakeANote doesn’t use the override keyword. Using an interface isn’t like overriding a virtual method in classes.

Class Pen might look like this:

class Pen : IRecordable {

public void TakeANote(string note) // Interface method // implementations

{ // MUST be declared public.

// ... scribble a note with a pen ... }

(8)

How to implement an interface

How to implement an interface

This example fulfills two requirements: Note that the class implements IRecordable, and provide a method implementation for TakeANote().

The syntax indicating that a class inherits a base class, such as ThingsThatRecord, is essentially no different from the syntax indicating that the class implements a C# interface such as IRecordable:

public class PDA : ThingsThatRecord ... public class PDA : IRecordable ...

Visual Studio can help you implement an interface. Hover the mouse pointer over the interface name in the class heading. A little underline appears underneath the first character of the interface name. Move the mouse until a menu opens and choose

(9)

How to implement an interface

How to implement an interface

How to name your interface

The .NET naming convention for interfaces precedes the name with the letter I. Interface names are typically adjectives, such as IRecordable.

Why C# includes interfaces

The bottom line of interfaces is that an interface describes a capability, such as Swim Safety Training or Class A Driver’s License. As a class, I earn my IRecordable badge when I implement the TakeANote ability.

(10)

Mixing

inheritance

and

interface

implementation

Mixing

inheritance

and

interface

implementation

Unlike some languages, such as C++, C# doesn’t allow multiple inheritancea class inheriting from two or more base classes. Think of class HouseBoat inheriting from House and Boat. Just don’t think of it in C#.

But although a class can inherit from only one base class, it can in addition implement as many interfaces as needed. After I treated recordability as an interface, a couple of the recording devices looked like this:

public class Pen : IRecordable // Base class is Object. {

public void TakeANote(string note) {

// Record the note with a pen. }

}

public class PDA : ElectronicDevice, IRecordable {

public void TakeANote(string note) {

// Record the note with your thumbs or a stylus. }

}

(11)

And here’s the payoff

And here’s the payoff

To begin to see the usefulness of an interface such as IRecordable, consider this example:

public class Program {

static public void RecordShoppingList(IRecordable recorder)

{

// Jot it down, using whatever device was passed in.

recorder.TakeANote(...);

}

public static void Main(string[] args) {

PDA pda = new PDA();

RecordShoppingList(pda); // Oops, battery’s low...

RecordShoppingList(pen); }

}

(12)

And here’s the payoff

And here’s the payoff

That concept is immensely powerful because it lets the RecordShoppingList() method be highly general — and thus possibly reusable in other programs. The method is even more general than using a base class such as ElectronicDevice for the argument type, because the interface lets you pass almost arbitrary objects that don’t necessarily have anything in common other than implementing the interface. They don’t even have to come from the same class hierarchy, which truly simplifies the designing of hierarchies, for example.

Programmers use the term interface in more than one way. You can see the C#

keyword interface and how it’s used. People also talk about a class’s public interface, or the public methods and properties that it exposes to the outside world. I keep the distinction clear by saying C# interface most of the time when that’s what I mean, and saying public interface when I refer to a class’s set of public methods.

(13)

Using an Interface

Using an Interface

In addition to your being able to use a C# interface for a parameter type, an interface is useful as

A method return type

The base type of a highly general array or collection

(14)

As a method return type

As a method return type

I like to farm out to a factory method the task of creating the key objects I need. Suppose that I have a variable like this one:

IRecordable recorder = null;

// Yes, you can have interface-type variables.

Somewhere, maybe in my constructor, I call a factory method to deliver a particular kind of IRecordable object:

recorder = MyClass.CreateRecorder(“Pen”); // A factory method is often static.

where CreateRecorder() is a method, often on the same class, that returns not a reference to a Pen but, rather, an IRecordable reference:

static IRecordable CreateRecorder(string recorderType)

{

if(recorderType == “Pen”) return new Pen(); ...

}

(15)

As the base type of an array or collection

As the base type of an array or collection

Suppose that you have two classes, Animal and Robot, and that both are abstract. You want to set up an array to hold both thisCat (an Animal) and thatRobot (a cute droid). The only way is to fall back on type Object, the ultimate base class in C#, and the only base class that’s common to both Animal and Robot as well as to their subclasses:

object[] things = new object[] { thisCat, thatRobot };

That’s poor design for lots of reasons. But suppose that you’re focused on the objects’ movements. You can have each class implement an IMovable interface:

interface IMovable {

void Move(int direction, int speed, int distance); }

and then set up an array of IMovables to manipulate your otherwise incompatible objects:

IMovable[] movables = { thisCat, thatRobot };

(16)

As a more general type of object reference

As a more general type of object reference

The following variable declaration refers to a specific, physical, concrete object:

Cat thisCat = new Cat();

One alternative is to use a C# interface for the reference:

IMovable thisMovableCat = (IMovable)new Cat();

// Note the required cast.

(17)

Abstract or concrete: When to use an abstract class and when to use an interface

Abstract or concrete: When to use an abstract class and when to use an interface

Concrete objects are instances of concrete classes. (A concrete class is a class that you can instantiate. It lacks the abstract keyword, and it implements all methods.)

You can represent abstractions in two ways in C#: with abstract classes or with C# interfaces. The two have differences that can affect your choice of which one to use:

Use an abstract class when you can profitably share an implementation with subclasses — the abstract base class can contribute real code that its subclasses can use by inheritance. For instance, maybe class Robot can handle part of the robot’s tasks, just not movement.

(18)

Abstract or concrete: When to use an abstract class and when to use an interface

Abstract or concrete: When to use an abstract class and when to use an interface

Use an interface when you can’t share any implementation or your implementing class already has a base class.

C# interfaces are purely, totally abstract. A C# interface supplies no implementation of any of its methods. Yet it can also add flexibility that isn’t otherwise possible. The abstract class option may not be available because you want to add a capability to a class that already has a base class (that you can’t modify). For example, class Robot may already have a base class in a library that you didn’t write and therefore can’t alter. Interfaces are especially helpful for representing completely abstract capabilities, such as movability or

(19)

Interface definition

Interface definition

An interface is nothing more than a named set of abstract members.

Abstract methods are pure protocol in that they do not provide a default implementation. The specific members defined by an interface depend on the exact behavior it is modeling. Yes, it’s true.

An interface expresses a behavior that a given class or structure may choose to implement.

Class (or structure) can support as many interfaces as necessary, thereby supporting (in essence) multiple behaviors.

// This interface defines the behavior of "having points" public interface IPointy

{

// Implicitly public and abstract. byte GetNumberOfPoints();

(20)

Interface features

Interface features

At a syntactic level, an interface is defined using the C# interface keyword. Unlike other .NET types, interfaces never specify a base class (not even System.Object) and their members never specify an access modifier (as all interface members are implicitly public and abstract).

// This interface defines the behavior of "having points." public interface IPointy

{

// Implicitly public and abstract. byte GetNumberOfPoints();

}

Notice that when you define interface members, you do not define an implementation scope for the member in question. Interfaces are pure protocol, and therefore never define an implementation (that is up to the supporting class or structure). Therefore, the following version of IPointy would result in various compiler errors:

public interface IPointy {

// Error! Interfaces cannot have fields! public int numbOfPoints;

// Error! Interfaces do not have constructors! public IPointy() { numbOfPoints = 0;};

// Error! Interfaces don't provide an implementation! byte GetNumberOfPoints() { return numbOfPoints; }

(21)

Interface features

Interface features

In any case, this initial IPointy interface defines a single method. However, .NET interface types are also able to define any number of property prototypes. For example, you could create the IPointy interface to use a read-only property rather than a traditional accessor method:

// The pointy behavior as a read-only property. public interface IPointy

{

byte Points{ get; } }

Do understand that interface types are quite useless on their own, as they are nothing more than a named collection of abstract members. For example, you cannot allocate interface types as you would a class or structure:

static void Main(string[] args) {

(22)

Implementing an interface

Implementing an interface

// This class derives from System.Object and // implements a single interface.

public class Pencil : IPointy {...}

// This class also derives from System.Object // and implements a single interface.

public class SwitchBlade : object, IPointy {...}

// This class derives from a custom base class // and implements a single interface.

public class Fork : Utensil, IPointy {...}

// This struct implicitly derives from System.ValueType and // implements two interfaces.

public struct Arrow : IClonable, IPointy {...}

When a class (or structure) chooses to extend its functionality by supporting interface types, it does so using a comma-delimited list in the type definition. Be aware that the direct base class must be the first item listed after the colon operator. When your class type derives directly from System. Object, you are free to simply list the interface(s) supported by the class, as the C# compiler will extend your types from System.Object if you do not say otherwise. On a related note, given that structures always derive from System.ValueType, simply list each interface directly after the structure definition.

(23)

Implementing an interface

Implementing an interface

// The abstract base class of the hierarchy. abstract class Shape

{

protected string shapeName; public Shape()

{

shapeName = "NoName"; }

public Shape(string s) {

shapeName = s; }

// A single virtual method. public virtual void Draw() {

Console.WriteLine("Inside Shape.Draw()"); }

public string PetName {

get { return shapeName; } set { shapeName = value; } }

} // New Shape derived class named Triangle.

public class Triangle : Shape, IPointy {

public Triangle() { }

public Triangle(string name) : base(name) { } public override void Draw()

{

Console.WriteLine("Drawing {0} the Triangle", PetName); }

// IPointy Implementation. public byte Points

{

get { return 3; } }

}

// The pointy behavior as a read-only property. public interface IPointy

{

(24)

Implementing an interface

Implementing an interface

// The abstract base class of the hierarchy. abstract class Shape

{

protected string shapeName; public Shape()

{

shapeName = "NoName"; }

public Shape(string s) {

shapeName = s; }

// A single virtual method. public virtual void Draw() {

Console.WriteLine("Inside Shape.Draw()"); }

public string PetName {

get { return shapeName; } set { shapeName = value; } }

}

// The pointy behavior as a read-only property. public interface IPointy

{

byte Points{ get; } }

// Hexagon now implements IPointy. public class Hexagon : Shape, IPointy {

public Hexagon(){ }

public Hexagon(string name) : base(name){ } public override void Draw()

{ Console.WriteLine("Drawing {0} the Hexagon", PetName); }

// IPointy Implementation. public byte Points

{

get { return 6; } }

(25)

Invoking interface members at the object level

Invoking interface members at the object level

Now that you have a set of types that support the IPointy interface, the next question is how you interact with the new functionality. The most straightforward way to interact with functionality supplied by a given interface is to invoke the methods from the object level.

static void Main(string[] args) {

...

// Catch a possible InvalidCastException. Circle c = new Circle("Lisa");

IPointy itfPt = null; try

{

itfPt = (IPointy)c;

Console.WriteLine(itfPt.Points); }

catch (InvalidCastException e) { Console.WriteLine(e.Message); } Console.ReadLine();

}

The second way you can determine whether a given type supports an interface is to make use of the as keyword. If the object can be treated as the specified interface, you are returned a reference to the interface in question. If not, you receive a null reference.

static void Main(string[] args) {

...

// Can we treat hex2 as IPointy? Hexagon hex2 = new Hexagon("Peter"); IPointy itfPt2 = hex2 as IPointy; if (itfPt2 != null)

Console.WriteLine("Points: {0}", itfPt2.Points); else

Console.WriteLine("OOPS! Not pointy..."); Console.ReadLine();

(26)

Obtaining interface references: the is keyword

Obtaining interface references: the is keyword

You may also check for an implemented interface using the is keyword. If the object in question is not compatible with the specified interface, you are returned the value false. On the other hand, if the type is compatible with the interface in question, you can safely call the members without needing to make use of try/catch logic.

static void Main(string[] args) {

Console.WriteLine("***** Fun with Interfaces *****\n"); // Make an array of Shapes.

Shape[] s = { new Hexagon(), new Circle(), new Triangle("Joe"), new Circle("JoJo")} ; for(int i = 0; i < s.Length; i++)

{

// Recall the Shape base class defines an abstract Draw() // member, so all shapes know how to draw themselves. s[i].Draw();

// Who's pointy? if (s[i] is IPointy)

Console.WriteLine("-> Points: {0}", ((IPointy)s[i]).Points); else

Console.WriteLine("-> {0}\'s not pointy!", s[i].PetName); Console.WriteLine();

}

(27)

Interfaces as parameters

Interfaces as parameters

Assume you have defined another interface named IDraw3D:

// Models the ability to render a type in stunning 3D. public interface IDraw3D

{

void Draw3D(); }

Next, assume that two of your three shapes (Circle and Hexagon) have been configured to support this new behavior:

// Circle supports IDraw3D.

public class Circle : Shape, IDraw3D {

...

public void Draw3D()

{ Console.WriteLine("Drawing Circle in 3D!"); } }

// Hexagon supports IPointy and IDraw3D.

public class Hexagon : Shape, IPointy, IDraw3D {

...

public void Draw3D()

(28)

Interfaces as parameters

Interfaces as parameters

If you now define a method taking an IDraw3D interface as a parameter, you are able to effectively send in any object implementing IDraw3D (if you attempt to pass in a type not supporting the necessary interface, you receive a compile-time error). Consider the following method defined within your Program type:

// I'll draw anyone supporting IDraw3D. static void DrawIn3D(IDraw3D itf3d) {

Console.WriteLine("-> Drawing IDraw3D compatible type"); itf3d.Draw3D();

}

We could now test whether an item in the Shape array supports this new interface, and if so, pass it into the DrawIn3D() method for processing:

static void Main() {

Console.WriteLine("***** Fun with Interfaces *****\n");

Shape[] s = {new Hexagon(), new Circle(),

new Triangle(), new Circle("JoJo")}; for(int i = 0; i < s.Length; i++)

{ ...

// Can I draw you in 3D?

if (s[i] is IDraw3D) DrawIn3D((IDraw3D)s[i]); }

(29)

Interfaces as return values

Interfaces as return values

Interfaces can also be used as method return values. For example, you could write a method that takes any System.Object, checks for IPointy compatibility, and returns a reference to the extracted interface (if supported):

// This method tests for IPointy compatibility and, // if able, returns an interface reference.

static IPointy ExtractPointyness(object o) {

if (o is IPointy) return (IPointy)o; else return null;

}

We could interact with this method as follows:

static void Main(string[] args) {

...

// Attempt to get IPointy from array of ints. int[] myInts = {10, 20, 30};

IPointy itfPt = ExtractPointyness(myInts); if (itfPt != null)

Console.WriteLine("Object has {0} points.", itfPt.Points); else

Console.WriteLine("This object does not implement IPointy"); Console.ReadLine();

(30)

Resolving name clashes via explicit interface implementation

Resolving name clashes via explicit interface implementation

A single class or structure can implement any number of interfaces. Given this, there is always a possibility that you may implement interfaces that contain identically named members, and therefore have a name clash to contend with.

// Draw image to a Form. public interface IDrawToForm {

void Draw(); }

// Draw to buffer in memory. public interface IDrawToMemory {

void Draw(); }

// Render to the printer.

public interface IDrawToPrinter {

void Draw(); }

class Octagon : IDrawToForm, IDrawToMemory, IDrawToPrinter {

public void Draw() {

// Shared drawing logic.

Console.WriteLine("Drawing the Octagon..."); }

}

static void Main(string[] args) {

Console.WriteLine("***** Fun with Interface Name Clashes *****\n"); // All of these invocations call the

// same Draw() method!

Octagon oct = new Octagon(); oct.Draw();

IDrawToForm itfForm = (IDrawToForm)oct; itfForm.Draw();

IDrawToPrinter itfPriner = (IDrawToPrinter)oct; itfPriner.Draw();

IDrawToMemory itfMemory = (IDrawToMemory)oct; itfMemory.Draw();

(31)

Resolving name clashes via explicit interface implementation

Resolving name clashes via explicit interface implementation

Clearly, the sort of code required to render the image to a window is quite different from the code needed to render the image to a networked printer or a region of memory. When you implement a collection of interfaces that have identical members, you can resolve this sort of name clash using explicit interface implementation syntax.

class Octagon : IDrawToForm, IDrawToMemory, IDrawToPrinter {

// Explicitly bind Draw() implementations // to a given interface.

void IDrawToForm.Draw() {

Console.WriteLine("Drawing to form..."); }

void IDrawToMemory.Draw() {

Console.WriteLine("Drawing to memory..."); }

void IDrawToPrinter.Draw() {

Console.WriteLine("Drawing to a printer..."); }

}

As you can see, when explicitly implementing an interface member, the general pattern breaks down to

returnValue InterfaceName.MethodName(args)

static void Main(string[] args) {

Octagon oct = new Octagon();

// We now must use casting to access the Draw() members IDrawToForm itfForm = (IDrawToForm)oct;

itfForm.Draw();

// Shorthand notation if you don't need // the interface variable for later use. ((IDrawToPrinter)oct).Draw();

// Could also use the "as" keyword.

if (oct is IDrawToMemory) ((IDrawToMemory)oct).Draw(); Console.ReadLine();

(32)

Designing interface hierarchies

Designing interface hierarchies

Interfaces can be arranged into an interface hierarchy. Like a class hierarchy, when an interface extends an existing interface, it inherits the abstract members defined by the parent type(s). Of course, unlike class-based inheritance, derived interfaces never inherit true implementation. Rather, a derived interface simply extends its own definition with additional abstract members.

Interface hierarchies can be useful when you wish to extend the functionality of an existing interface without breaking existing code bases.

public interface IDrawable {

void Draw(); }

Given that IDrawable defines a basic drawing behavior, we could now create a derived interface that extends this type with the ability to render its output to the printer. Assume this method is called Print():

public interface IPrintable : IDrawable {

void Print(); }

We could define a final interface named IRenderToMemory, which extends IPrintable with a new member named Render():

public interface IRenderToMemory : IPrintable {

(33)

Designing interface hierarchies

Designing interface hierarchies

Given this design, if a type were to implement IRenderToMemory, we would now be required to implement each and every member defined up the chain of inheritance (specifically, the Render(), Print(), and Draw() methods). On the other hand, if a type were to only implement IPrintable, we would only need to contend with Print() and Draw().

public class SuperShape : IRenderToMemory {

public void Draw() { Console.WriteLine("Drawing..."); } public void Print() { Console.WriteLine("Printing..."); } public void Render() { Console.WriteLine("Rendering..."); } }

Now, when we make use of the SuperShape, we are able to invoke each method at the object level (as they are all public) as well as extract out a reference to each supported interface explicitly via casting:

static void Main(string[] args) {

Console.WriteLine("***** The SuperShape *****");

// Call from object level.

SuperShape myShape = new SuperShape(); myShape.Draw();

// Get IPrintable explicitly. // (and IDrawable implicitly!) IPrintable iPrint;

iPrint = (IPrintable)myShape; iPrint.Draw();

(34)

Multiple inheritance with interface types

Multiple inheritance with interface types

Unlike class types, it is possible for a single interface to extend multiple base interfaces. This allows us to design some very powerful and flexible abstractions.

// Multiple inheritance for interface types is a-okay. public interface IDrawable

{

void Draw(); }

public interface IPrintable {

void Print();

void Draw(); // <-- Note possible name clash here! }

// Multiple interface inheritance. OK!

public interface IShape : IDrawable, IPrintable {

int GetNumberOfSides(); }

Now, the million dollar question is, if we have a class supporting IShape, how many methods will it be required to implement? The answer: it depends. If we wish to provide a simple implementation of the Draw() method, we only need to provide three members

class Rectangle : IShape {

public int GetNumberOfSides() { return 4; }

(35)

Multiple inheritance with interface types

Multiple inheritance with interface types

If you would rather have specific implementations for each Draw() method (which in this case would make the most sense), you can resolve the name clash using explicit interface implementation, as shown in the following Square type:

class Square : IShape {

// Using explicit implementation to handle member name clash. void IPrintable.Draw()

{ // Draw to printer ... }

void IDrawable.Draw() { // Draw to screen ... }

public void Print() { // Print ... }

public int GetNumberOfSides() { return 4; }

}

Interfaces can be extremely useful when

 You have a single hierarchy where only a subset of the derived types support a common behavior.

(36)

References

Related documents

In the following year, (Alvarez-Chavez et al., 2000) reported on the actively Q-switched Yb 3+ - doped fiber laser which is capable of generating a 2.3 mJ of output pulse energy at

Investigating the Relationship between Ethical Leadership and Deviant Behaviors in the Workplace: The Mediating Role of Emotional Commitment and Moral Moral, Organizational

At the northern endpoint of the trail, this space marks a gateway to the Lake Itasca Greenway with one of two proposed sculptural arches along the corridor.. This arch stands

One point more than 3.00 sigmas from center line... One point more than 3.00 sigmas from

For example, we can combine the base form have with the past participle of be  been together with the -ing form of a lexical verb in order to create a verb phrase that describes

In one case we learned of a program whose government coun- terparts were active participants in the various software sprint and release reviews and acting as product owners, while

Akbar, “Augmented Reality Technology Implementation in Android-Based Interactive Magazine Using the FAST Corner Detection Algorithm Method (in Bahasa: Implementasi Teknologi Augmented

Concerning the interactions between gender and external context, we find that when the competitive pressure increases, female-led banks experience lower performance than