Skip to content

Method Stereotypes

Ali Al-Ramadan edited this page Feb 2, 2024 · 15 revisions

What is a Method Stereotype?

A method stereotype is a concise high-level description of the role of a method in a software system. It is a simple form of documentation that is used to provide additional information about the behavioral aspects and responsibilities of a method. Method stereotype information can be used to lay the foundation of more sophisticated forms of documentations and design recovery.[1][2]

Some uses of method stereotype information include determining the stereotypes of classes,[5] improving the precision of metrics calculations,[1] measuring the level of improvement or degradation of a method when its role changes,[3] improving feature location in source code,[4] generating natural language summaries for methods,[6] characterizing commits on version control systems into different types, [7] and creating signature descriptors for software systems.[8]

Taxonomy of Method Stereotypes

Method stereotypes can be classified into five main categories depending on the role of a method:

Stereotype Category Stereotype Description
Structural Accessor get Returns a data member
non-const-get (C++)
predicate Returns Boolean value that is not a data member
non-const-predicate (C++)
property Returns non-boolean value that is not a data member
non-const-property (C++)
accessor Returns information about data members through parameters
non-const-accessor (C++)
Structural Mutator set Modifies a data member
command Performs a complex change to the object’s state.
non-void-command
Creational constructor Creates and/or destroys objects
copy-constructor
destructor
factory
Collaborational collaborator Works with objects belonging to classes other than itself. Changes the state of the current object
controller Works with objects belonging to classes other than itself. Changes only an external object’s state (not this)
Degenerate stateless (a.k.a incidental or pure stateless) Does not read or change an object’s state directly nor indirectly. Has no calls of any type
wrapper (a.k.a stateless) Does not read or change an object’s state directly. Has one call of any type
empty Has no statements

TABLE 1. Taxonomy of Method Stereotypes.

Structural Accessors

A structural accessor is a method that returns information about the state of an object. There are four types: get, predicate, property, and void-accessor.

  • get

A get is a method returns the value of some data member (field or attribute).

A non-const-get method (C++ only) is a get method that is not constant.

Examples for C++:

// @stereotype get
double AxisModelBase::getTickStep() const {
  return m_tick_step;
}

// @stereotype get
bool RootBranch::isUseable() const {
  return m_useable;
}

// @stereotype non-const-get
std::list<std::string>& XmlController::getMissingTuples( ) {
  return m_missing_tuples;
}

Examples for C#:

// @stereotype get
protected override string GetSourceObjectEventName() 
{
    return _eventName;
}

internal string Namespace 
{
// @stereotype get
    get 
    {
        return this.nameSpace;
    }
}

// @stereotype get
internal int Buffercapacity() 
{
    return _capacity;
}

Examples for Java:

// @stereotype get
public final boolean isTimeout() {
    return timeout;
}

// @stereotype get
public static boolean isLockdown() {
    return lockdown;
}

// @stereotype get
long cachedEventCount() {
    return size;
}
  • predicate

A predicate is a method that computes a Boolean value using data member values and returns it.

A non-const-predicate method (C++ only) is a predicate method that is not constant.

Examples for C++:

// @stereotype predicate 
bool RootBranch::isMultiDimensional() const {
  return !(m_number_elements == 1 );
}

// @stereotype predicate 
bool RepBase::isSelected() const {
  return m_desel == false;
}

// @stereotype non-const-predicate 
bool QtFileDialog::isDocSuffix(const std::string & suffix ) {
  return suffix == s_doc_suffix || suffix == ".xml";
}

Examples for C#:

// @stereotype predicate
internal bool IsPassThruObjectNeeded() 
{
    return (_passThru) && (!this.DidUserSuppressTheOperation) && (!this.JobHadErrors);
}

// @stereotype predicate
internal bool IsInsideCatchBlock(int index)
{
    return index >= HandlerStartIndex && index < HandlerEndIndex;
}

// @stereotype predicate
internal bool HostInNestedPrompt() 
{
    if (NestedPromptCount > 0) 
    {
        return true;
    }
    else 
    {
        return false;
    }
}

Examples for Java:

// @stereotype predicate
public boolean isCancelled() {
    return get() == CANCELLED;
}

// @stereotype predicate
public final boolean isEmpty() {
    return get() != FUSED_READY;
}

// @stereotype predicate
public final boolean isDisposed() {
    return get() == DISPOSED;
}
  • property

A property is a method that returns information (not Boolean) about an object using data member values.

A non-const-property method (C++ only) is a property method that is not constant.

// @stereotype property
inline double calcRed(double value) const {
    if(m_parms[SIGMA] == 0.0) {
      Erfc* p = const_cast<Erfc*>(this);
      p->m_parms[SIGMA] = 0.0001;
    }
    return (value - m_parms[MEAN])/m_parms[SIGMA];
}

// @stereotype property
double Range::fraction(double value) const {
  return (value - m_min)/(m_max - m_min);
}

// @stereotype non-const-property
size_t count() { 
    return count_ + this->size();
}

Examples for C#:

// @stereotype property
internal int ColumnFromOffset(int offset)
{
    return offset - _lineStartMap[LineFromOffset(offset) - 1] + 1;
}

// @stereotype property
public override int Peek()
{
    if (_charCount == 0)
    {
        if (RefillCharBuffer() == -1)
        {
            return -1;
        }
    }
    return (int)_charBuff[_charCount - 1];
}

Examples for Java:

// @stereotype property
static int mix(int x) {
    final int h = x * INT_PHI;
    return h ^ (h >>> 16);
}

// @stereotype property
int calcElementOffset(long index) {
    return (int)index & mask;
}
  • accessor

An accessor is a method that returns information about an object through a parameter. This covers the more general case where the method could return information about the attributes using the parameters and the method return.

A non-const-accessor method (C++ only) is a accessor method that is not constant.

Examples for C++:

// @stereotype accessor
virtual void next_bytes(uint8_t* buffer, size_t buffer_size) const {
    if (buffer == nullptr) throw argument_null_exception(current_stack_frame_);
    for (size_t index = 0; index < buffer_size; index++)
        buffer[index] = next<uint8_t>(0, std::numeric_limits<uint8_t>::max());
}

// @stereotype accessor
void DoGetClientSize(int* width, int* height) const override {
  wxStaticBox::DoGetSize(width, height);
  *width = *width - GetClientAreaOrigin().x - inner_margin;
  *height = *height - GetClientAreaOrigin().y - inner_margin;
}

// @stereotype non-const-accessor
inline void visible_cells(int& r1, int& r2, int& c1, int& c2) {
    r1 = toprow;
    r2 = botrow;
    c1 = leftcol;
    c2 = rightcol;
} 

Examples for C#:

// @stereotype accessor
internal bool IsFuzzyMatch(string candidate, string pattern, out int score)
{
    score = GetDamerauLevenshteinDistance(candidate, pattern);
    return score <= MinimumDistance;
}

// @stereotype accessor
public static string GetMissingEventMessage(out int parameterCount)
{
    parameterCount = 1;
    return MissingEventIdResourceName;
}

Examples for Java:

// @stereotype accessor
void dequantize(int[] dataUnit, int iComp) {
	int[] qTable = quantizationTables[frameComponents[componentIds[iComp]][TQI]];
	for (int i = 0; i < dataUnit.length; i++) {
		int zzIndex = ZigZag8x8[i];
		dataUnit[zzIndex] = dataUnit[zzIndex] * qTable[i];
	}
}

Structural Mutators

A structural mutator is a method that modifies the state of an object. There are three types: set, command, and non-void-command.

  • set

A set method is a mutator that modifies the value of a data member.

Examples for C++;

// @stereotype set 
void dataSource::setName(const string& name){
  m_ds_name = name;
}

// @stereotype set
void ListTuple::setShape(std::vector<unsigned int>& shape) {
  m_shape = shape;
}

Examples for C#:

// @stereotype set 
protected virtual void Dispose(bool disposing)
{
    if (_disposed)
    {
        return;
    }
    _disposed = true;
}


internal TypeTable TypeTable
{
    // @stereotype set 
    set { _serializer.TypeTable = value; }
}

Examples for Java:

// @stereotype set 
public void setDone() 
{
    this.done = true;
}

// @stereotype set 
public void accept(Integer t) 
{
    items++;
}
  • command

A command method is a mutator that modifies the state of an object in a complex way.

Examples for C++:

// @stereotype command
void Cursor::_RedrawCursor() noexcept {
    if (IsOn() && !IsConversionArea()) {
        if (_fDeferCursorRedraw) {
            _fHaveDeferredCursorRedraw = true;
        }
        else {
            _RedrawCursorAlways();
        }
    }
}

// @stereotype non-void-command
basic_memory_buffer& operator=(basic_memory_buffer&& other) FMT_NOEXCEPT {
    FMT_ASSERT(this != &other, "");
    deallocate();
    move(other);
    return *this;
}

Examples for C#:

// @stereotype command
private void WriteNameAttribute(string value)
{
    Dbg.Assert(!string.IsNullOrEmpty(value), "Caller should validate the parameter");
    WriteAttribute(
        SerializationStrings.NameAttribute,
        EncodeString(value));
}

// @stereotype non-void-command
private static ulong ConvertToUlong(double val)
{
    if (val < 0)
    {
        long lValue = LanguagePrimitives.ConvertTo<long>(val);
        return unchecked((ulong)lValue);
    }

    return LanguagePrimitives.ConvertTo<ulong>(val);
}

Examples for Java:

// @stereotype command
public final void subscribe(@NonNull CompletableObserver observer) {
    Objects.requireNonNull(observer, "observer is null");
    try {
        observer = RxJavaPlugins.onSubscribe(this, observer);
        Objects.requireNonNull(observer, "The RxJavaPlugins.onSubscribe hook returned a null CompletableObserver. Please check the handler provided to RxJavaPlugins.setOnCompletableSubscribe for invalid null returns. Further reading: https://github.com/ReactiveX/RxJava/wiki/Plugins");
        subscribeActual(observer);
    } catch (NullPointerException ex) { 
        throw ex;
    } catch (Throwable ex) {
        Exceptions.throwIfFatal(ex);
        RxJavaPlugins.onError(ex);
        throw toNpe(ex);
    }
}

// @stereotype non-void-command
public static <@NonNull T> Observable<T> mergeDelayError(
        @NonNull ObservableSource<? extends T> source1, @NonNull ObservableSource<? extends T> source2) {
    Objects.requireNonNull(source1, "source1 is null");
    Objects.requireNonNull(source2, "source2 is null");
    return fromArray(source1, source2).flatMap((Function)Functions.identity(), true, 2);
}
  • factory

A factory method is a creational method that creates an object and returns it to the client.

Examples for C++:

// @stereotype factory
CutRangeRep* CutRangeRep::clone() {
  return new CutRangeRep(*this);
}

// @stereotype factory
CompositePlotter* CompositePlotter::clone () {
  return new CompositePlotter(*this);
}

Examples for C#:

// @stereotype factory
protected CimSessionProxy CreateCimSessionProxy(CimSessionProxy originalProxy, bool passThru)
{
    CimSessionProxy proxy = new CimSessionProxySetCimInstance(originalProxy, passThru);
    this.SubscribeEventAndAddProxytoCache(proxy);
    return proxy;
}

// @stereotype factory
internal static PSTraceSource GetNewTraceSource(string name, string description, bool traceHeaders)
{
    ArgumentException.ThrowIfNullOrEmpty(name);
    string fullName = name;
    PSTraceSource result = new PSTraceSource(fullName, name, description, traceHeaders);s
    return result;
}

Examples for Java:

// @stereotype factory
public final Completable cache() {
    return RxJavaPlugins.onAssembly(new CompletableCache(this));
}

// @stereotype factory
public static <T> UnicastSubject<T> create(boolean delayError) {
    return new UnicastSubject<>(bufferSize(), null, delayError);
}

Collaborational

Collaborational methods deal with objects of other classes. There are two types: collaborator, and controller.

  • collaborator

A collaborator method works on objects that belong to classes other than itself. These objects are usually passed as parameters, created as local variables, or accessed indirectly through a pointer/reference from some data member.

Examples for C++:

// @stereotype collaborator
template<typename T>
void copy_to(T* other) const
{
    other->group(group_);
    other->required(required_);
    other->ignore_case(ignore_case_);
    other->ignore_underscore(ignore_underscore_);
    other->configurable(configurable_);
    other->disable_flag_override(disable_flag_override_);
    other->delimiter(delimiter_);
    other->always_capture_default(always_capture_default_);
    other->multi_option_policy(multi_option_policy_);
}

// @stereotype collaborator
inline std::string Formatter::make_positionals(const App* app) const
{
    std::vector<const Option*> opts =
        app->get_options([](const Option* opt) { return !opt->get_group().empty() && opt->get_positional(); });

    if (opts.empty())
        return std::string();

    return make_group(get_label("Positionals"), true, opts);
}

Examples for C#:

// @stereotype collaborator
private void OnNotifyPropertyChanged(string propertyName)
{
    PropertyChangedEventHandler handler = this.PropertyChanged;
    if (handler != null)
    {
        handler(this, new PropertyChangedEventArgs(propertyName));
    }
}

// @stereotype collaborator
protected virtual void NotifyFilterExpressionChanged()
{
    EventHandler eh = this.FilterExpressionChanged;
    if (eh != null)
    {
        eh(this, new EventArgs());
    }
}

Examples for Java:

// @stereotype collaborator
public void printStackTrace(PrintStream s) {
    printStackTrace(new WrappedPrintStream(s));
}
  • controller

A controller method works only on objects that belong to classes other than itself.

Examples for C++:

// @stereotype controller
inline std::string Formatter::make_option_name(const Option* opt, bool is_positional) const
{
    if (is_positional)
        return opt->get_name(true, false);

    return opt->get_name(false, true);
}

// @stereotype controller
bool operator()(const Font& lhs, const Font& rhs) const
{
    return lhs.LocalizedName() < rhs.LocalizedName();
}

Examples for C#:

// @stereotype controller
private void ViewModel_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
    if (e.PropertyName == "Zoom")
    {
        HelpWindowSettings.Default.HelpZoom = this.viewModel.Zoom;
    }
}

Examples for Java:

// @stereotype controller
public void flowFlatMapIterable1(Blackhole bh) {
    flowFlatMapIterable1.subscribe(new PerfConsumer(bh));
}

Degenerate

A degenerate method is a method that has no use of the object's state. There are three types: stateless, wrapper, and empty[8]

  • stateless (a.k.a incidental or pure stateless)

A stateless method is a method that does not read or change an object’s state directly nor indirectly and does not make calls to other methods of the same class.

Examples for C++:

// @stereotype stateless
point add(const point& n1, const point& n2) {
  return n1 + n2;
}

// @stereotype stateless
bool NTupleChiSqFCN::needsIntegrated() const {
  return false;
}

Examples for C#:

// @stereotype stateless
protected override string GetSourceObjectEventName()
{
    return "CimIndicationArrived";
}

// @stereotype stateless
private static string GetPipePath(string pipeName)
{
    // Platform is a static class
    if (Platform.IsWindows)
    {
        return $@"\\.\pipe\{pipeName}";
    }
    
    // Path is a static class
    return $@"{Path.GetTempPath()}CoreFxPipe_{pipeName}";
}

Examples for Java:

// @stereotype stateless
public long maxElementsFromPublisher() {
    return 1;
}

// @stereotype stateless
public void cleanup() {
  // Scheduler is a static field defined in a different class that is not inherited
  Scheduler.IS_DRIFT_USE_NANOTIME = false;
}
  • wrapper (a.k.a stateless)

A wrapper method is a method that does not read or change an object’s state directly. This method can have one call of any kind (method call, function call, or a constructor call)

Examples for C++:

// @stereotype wrapper
virtual double errorDef () const {
    return up();
}

// @stereotype wrapper
bool _isConnected() const noexcept {
    // ConnectionState is an enum
    return _isStateOneOf(ConnectionState::Connected);
}

Examples for C#:

// @stereotype wrapper
public override void EnterNestedPrompt()
{
    EnterNestedPrompt(null);
}

// @stereotype wrapper
public override void WriteDebugLine(string message)
{
    // message is not a field
    WriteDebugLineHelper(message);
}

Examples for Java:

// @stereotype wrapper
public void simpleOneLessAsyncLoop() {
    for (int i = 0; i < 200; i++) {
        simpleOneLessAsync();
    }
}

// @stereotype wrapper
public void onComplete() {
    countDown();
}
  • empty

An empty is a method that has no statements (except for comments).

Examples for C++:

// @stereotype empty
void hippodraw::Observer::willDelete(const Observable*) {
  // do nothing, default behaviour
}

Examples for C#:

// @stereotype empty
internal virtual void RemoveCommandTransportManager(Guid powerShellCmdId)
{

}

Examples for Java:

// @stereotype empty
public void onSubscribe(Disposable d) 
{

}

Multiple Stereotypes

A method can be labeled with one or more stereotypes from each category.

Examples for C++:

// @stereotype non-void-command factory collaborator
plotterBase* displayController::createDisplay(const string& name){
  dataRepController* controller = dataRepController::instance();
  dataRep* rep = controller->createDataRep(name);

  return createDisplay(rep);
}

Examples for C#:

// @stereotype non-void-command collaborator
public static int Main(string[] args)
{
    using (PowerShell ps = PowerShell.Create())
    {
        Console.WriteLine("\nEvaluating 'Get-Command Write-Output' in PS Core Runspace\n");
        var results = ps.AddScript("Get-Command Write-Output").Invoke();
        Console.WriteLine(results[0].ToString());

        ps.Commands.Clear();

        Console.WriteLine("\nEvaluating '([S.M.A.ActionPreference], [S.M.A.AliasAttribute]).FullName' in PS Core Runspace\n");
        results = ps.AddScript("([System.Management.Automation.ActionPreference], [System.Management.Automation.AliasAttribute]).FullName").Invoke();
        foreach (dynamic result in results)
        {
            Console.WriteLine(result.ToString());
        }
    }

    return 0;
}

Examples for Java:

// @stereotype factory controller wrapper
public static <T> Predicate<T> equalsWith(T value) {
    return new EqualsPredicate<>(value);
}

References

[1] Dragan, N., Collard, M. L., & Maletic, J. I. (2006, September). Reverse engineering method stereotypes. In 2006 22nd IEEE International Conference on Software Maintenance (pp. 24-34). IEEE.

[2] Guarnera, D., Collard, M. L., Dragan, N., Maletic, J. I., Newman, C., & Decker, M. (2018, September). Automatically Redocumenting Source Code with Method and Class Stereotypes. In 2018 IEEE Third International Workshop on Dynamic Software Documentation (DySDoc3) (pp. 3-4). IEEE.

[3] Decker, M. J., Newman, C. D., Dragan, N., Collard, M. L., Maletic, J. I., & Kraft, N. A. (2018, May). A taxonomy of how method stereotypes change. In Proceedings of the 40th International Conference on Software Engineering: Companion Proceeedings (pp. 337-338).

[4] Alhindawi, N., Dragan, N., Collard, M. L., & Maletic, J. I. (2013, September). Improving feature location by enhancing source code with stereotypes. In 2013 IEEE International Conference on Software Maintenance (pp. 300-309). Ieee.

[5] Dragan, N., Collard, M. L., & Maletic, J. I. (2010, September). Automatic identification of class stereotypes. In 2010 IEEE International Conference on Software Maintenance (pp. 1-10). IEEE.

[6] Abid, N. J., Dragan, N., Collard, M. L., & Maletic, J. I. (2015, September). Using stereotypes in the automatic generation of natural language summaries for c++ methods. In 2015 IEEE International Conference on Software Maintenance and Evolution (ICSME) (pp. 561-565). IEEE.

[7] Dragan, N., Collard, M. L., Hammad, M., & Maletic, J. I. (2011, September). Using stereotypes to help characterize commits. In 2011 27th IEEE International Conference on Software Maintenance (ICSM) (pp. 520-523). IEEE.

[8] Dragan, N., Collard, M. L., & Maletic, J. I. (2009, September). Using method stereotype distribution as a signature descriptor for software systems. In 2009 IEEE International Conference on Software Maintenance (pp. 567-570). IEEE.

Clone this wiki locally